Browse Source

Code modernization and PSR-2

Curtis Farnham 7 years ago
parent
commit
ab365ef558

+ 1 - 0
.gitignore

@@ -2,3 +2,4 @@ composer.phar
 composer.lock
 .DS_Store
 /vendor
+/scripts

+ 11 - 2
composer.json

@@ -28,8 +28,17 @@
 		"phpunit/phpunit": "~6.0"
     },
     "autoload": {
-        "psr-0": {
-            "RNCryptor": "lib/"
+        "psr-4": {
+            "RNCryptor\\": "lib/"
         }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\": "tests/"
+        }
+    },
+    "config": {
+        "preferred-install": "dist",
+        "sort-packages": true
     }
 }

+ 0 - 62
lib/RNCryptor/Autoloader.php

@@ -1,62 +0,0 @@
-<?php
-
-/*
- * This file is part of the Predis package.
- *
- * (c) Daniele Alessandri <suppakilla@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace RNCryptor;
-
-/**
- * Implements a lightweight PSR-0 compliant autoloader.
- *
- * @author Eric Naeseth <eric@thumbtack.com>
- * @author Daniele Alessandri <suppakilla@gmail.com>
- */
-class Autoloader
-{
-    private $directory;
-    private $prefix;
-    private $prefixLength;
-
-    /**
-     * @param string $baseDirectory Base directory where the source files are located.
-     */
-    public function __construct($baseDirectory = __DIR__)
-    {
-        $this->directory = $baseDirectory;
-        $this->prefix = __NAMESPACE__ . '\\';
-        $this->prefixLength = strlen($this->prefix);
-    }
-
-    /**
-     * Registers the autoloader class with the PHP SPL autoloader.
-     *
-     * @param bool $prepend Prepend the autoloader on the stack instead of appending it.
-     */
-    public static function register($prepend = false)
-    {
-        spl_autoload_register(array(new self, 'autoload'), true, $prepend);
-    }
-
-    /**
-     * Loads a class from a file using its fully qualified name.
-     *
-     * @param string $className Fully qualified name of a class.
-     */
-    public function autoload($className)
-    {
-        if (0 === strpos($className, $this->prefix)) {
-            $parts = explode('\\', substr($className, $this->prefixLength));
-            $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
-
-            if (is_file($filepath)) {
-                require($filepath);
-            }
-        }
-    }
-}

+ 124 - 156
lib/RNCryptor/Cryptor.php

@@ -1,165 +1,133 @@
 <?php
-namespace RNCryptor;
-
-class Cryptor {
-
-	const DEFAULT_SCHEMA_VERSION = 3;
-
-	protected $_settings;
-
-	public function __construct() {
-		if (!extension_loaded('openssl')) {
-			throw new \Exception('RNCryptor requires the openssl extension.');
-		}
-	}
-
-	protected function _configureSettings($version) {
-
-		$settings = new \stdClass();
-
-		$settings->algorithm = 'aes-256-';
-		$settings->saltLength = 8;
-		$settings->ivLength = 16;
-
-		$settings->pbkdf2 = new \stdClass();
-		$settings->pbkdf2->prf = 'sha1';
-		$settings->pbkdf2->iterations = 10000;
-		$settings->pbkdf2->keyLength = 32;
-		
-		$settings->hmac = new \stdClass();
-		$settings->hmac->length = 32;
-
-		switch ($version) {
-			case 0:
-				$settings->mode = 'ctr';
-				$settings->options = 0;
-				$settings->hmac->includesHeader = false;
-				$settings->hmac->algorithm = 'sha1';
-				$settings->hmac->includesPadding = true;
-				$settings->truncatesMultibytePasswords = true;
-				break;
-
-			case 1:
-				$settings->mode = 'cbc';
-				$settings->options = 1;
-				$settings->hmac->includesHeader = false;
-				$settings->hmac->algorithm = 'sha256';
-				$settings->hmac->includesPadding = false;
-				$settings->truncatesMultibytePasswords = true;
-				break;
-
-			case 2:
-				$settings->mode = 'cbc';
-				$settings->options = 1;
-				$settings->hmac->includesHeader = true;
-				$settings->hmac->algorithm = 'sha256';
-				$settings->hmac->includesPadding = false;
-				$settings->truncatesMultibytePasswords = true;
-				break;
-
-			case 3:
-				$settings->mode = 'cbc';
-				$settings->options = 1;
-				$settings->hmac->includesHeader = true;
-				$settings->hmac->algorithm = 'sha256';
-				$settings->hmac->includesPadding = false;
-				$settings->truncatesMultibytePasswords = false;
-				break;
-
-			default:
-				throw new \Exception('Unsupported schema version ' . $version);
-		}
-
-		$this->_settings = $settings;
-	}
-
-    protected function _decrypt_internal($key, $payload, $mode, $iv = null) {
-
-        if ($iv == null) {
-            $iv = "";
+namespace RNCryptor\RNCryptor;
+
+use stdClass;
+
+class Cryptor
+{
+    const DEFAULT_SCHEMA_VERSION = 3;
+
+    protected $config;
+
+    public function generateKey($salt, $password, $version = self::DEFAULT_SCHEMA_VERSION)
+    {
+        $this->configure($version);
+
+        return $this->makeKey($salt, $password);
+    }
+
+    protected function aesCtrLittleEndianCrypt($payload, $key, $iv)
+    {
+        $numOfBlocks = ceil(strlen($payload) / strlen($iv));
+        $counter = '';
+        for ($i = 0; $i < $numOfBlocks; ++$i) {
+            $counter .= $iv;
+
+            // Yes, the next line only ever increments the first character
+            // of the counter string, ignoring overflow conditions.  This
+            // matches CommonCrypto's behavior!
+            $iv[0] = chr(ord($iv[0]) + 1);
         }
-        return openssl_decrypt($payload, $this->_settings->algorithm.$mode, $key, OPENSSL_RAW_DATA, $iv);
+
+        return $payload ^ $this->encryptInternal($key, $counter, 'ecb');
     }
 
-    protected function _encrypt_internal($key, $payload, $mode, $iv = null) {
+    protected function encryptInternal($key, $payload, $mode, $iv = null)
+    {
+        return openssl_encrypt($payload, $this->config->algorithm . $mode, $key, OPENSSL_RAW_DATA, (string)$iv);
+    }
 
-        if ($iv == null) {
-            $iv = "";
+    protected function makeHmac(stdClass $components, $hmacKey)
+    {
+        $hmacMessage = '';
+        if ($this->config->hmac->includesHeader) {
+            $hmacMessage .= ''
+                . $components->headers->version
+                . $components->headers->options
+                . (isset($components->headers->encSalt) ? $components->headers->encSalt : '')
+                . (isset($components->headers->hmacSalt) ? $components->headers->hmacSalt : '')
+                . $components->headers->iv;
         }
-        return openssl_encrypt($payload, $this->_settings->algorithm.$mode, $key, OPENSSL_RAW_DATA, $iv);
+
+        $hmacMessage .= $components->ciphertext;
+
+        $hmac = hash_hmac($this->config->hmac->algorithm, $hmacMessage, $hmacKey, true);
+
+        if ($this->config->hmac->includesPadding) {
+            $hmac = str_pad($hmac, $this->config->hmac->length, chr(0));
+        }
+    
+        return $hmac;
+    }
+
+    protected function makeKey($salt, $password)
+    {
+        if ($this->config->truncatesMultibytePasswords) {
+            $utf8Length = mb_strlen($password, 'utf-8');
+            $password = substr($password, 0, $utf8Length);
+        }
+
+        $algo = $this->config->pbkdf2->prf;
+        $iterations = $this->config->pbkdf2->iterations;
+        $length = $this->config->pbkdf2->keyLength;
+
+        return hash_pbkdf2($algo, $password, $salt, $iterations, $length, true);
     }
 
-	/**
-	 * Encrypt or decrypt using AES CTR Little Endian mode
-	 */
-	protected function _aesCtrLittleEndianCrypt($payload, $key, $iv) {
-
-		$numOfBlocks = ceil(strlen($payload) / strlen($iv));
-		$counter = '';
-		for ($i = 0; $i < $numOfBlocks; ++$i) {
-			$counter .= $iv;
-
-			// Yes, the next line only ever increments the first character
-			// of the counter string, ignoring overflow conditions.  This
-			// matches CommonCrypto's behavior!
-			$iv[0] = chr(ord($iv[0]) + 1);
-		}
-
-        return $payload ^ $this->_encrypt_internal($key, $counter, 'ecb');
-	}
-
-	protected function _generateHmac(\stdClass $components, $hmacKey) {
-	
-		$hmacMessage = '';
-		if ($this->_settings->hmac->includesHeader) {
-			$hmacMessage .= $components->headers->version
-							. $components->headers->options
-							. (isset($components->headers->encSalt) ? $components->headers->encSalt : '')
-							. (isset($components->headers->hmacSalt) ? $components->headers->hmacSalt : '')
-							. $components->headers->iv;
-		}
-
-		$hmacMessage .= $components->ciphertext;
-
-		$hmac = hash_hmac($this->_settings->hmac->algorithm, $hmacMessage, $hmacKey, true);
-
-		if ($this->_settings->hmac->includesPadding) {
-			$hmac = str_pad($hmac, $this->_settings->hmac->length, chr(0));
-		}
-	
-		return $hmac;
-	}
-
-	/**
-	 * Key derivation -- This method is intended for testing.  It merely
-	 * exposes the underlying key-derivation functionality.
-	 */
-	public function generateKey($salt, $password, $version = self::DEFAULT_SCHEMA_VERSION) {
-		$this->_configureSettings($version);
-		return $this->_generateKey($salt, $password);
-	}
-
-	public function generateSalt($version = self::DEFAULT_SCHEMA_VERSION) {
-		$this->_configureSettings($version);
-		return $this->_generateIv($this->_settings->saltLength);
-	}
-
-	protected function _generateKey($salt, $password) {
-
-		if ($this->_settings->truncatesMultibytePasswords) {
-			$utf8Length = mb_strlen($password, 'utf-8');
-			$password = substr($password, 0, $utf8Length);
-		}
-
-		return hash_pbkdf2($this->_settings->pbkdf2->prf, $password, $salt, $this->_settings->pbkdf2->iterations, $this->_settings->pbkdf2->keyLength, true);
-	}
-
-	private function _generateIv($blockSize) {
-		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
-			$randomSource = MCRYPT_RAND;
-		} else {
-			$randomSource = MCRYPT_DEV_URANDOM;
-		}
-		return mcrypt_create_iv($blockSize, $randomSource);
-	}
+    protected function configure($version)
+    {
+        $config = new stdClass;
+
+        $config->algorithm = 'aes-256-';
+        $config->saltLength = 8;
+        $config->ivLength = 16;
+
+        $config->pbkdf2 = new stdClass;
+        $config->pbkdf2->prf = 'sha1';
+        $config->pbkdf2->iterations = 10000;
+        $config->pbkdf2->keyLength = 32;
+
+        $config->hmac = new stdClass();
+        $config->hmac->length = 32;
+
+        if (!$version) {
+            $this->configureVersionZero($config);
+        } elseif ($version <= 3) {
+            $config->mode = 'cbc';
+            $config->options = 1;
+            $config->hmac->algorithm = 'sha256';
+            $config->hmac->includesPadding = false;
+
+            switch ($version) {
+                case 1:
+                    $config->hmac->includesHeader = false;
+                    $config->truncatesMultibytePasswords = true;
+                    break;
+
+                case 2:
+                    $config->hmac->includesHeader = true;
+                    $config->truncatesMultibytePasswords = true;
+                    break;
+
+                case 3:
+                    $config->hmac->includesHeader = true;
+                    $config->truncatesMultibytePasswords = false;
+                    break;
+            }
+        } else {
+            throw new \RuntimeException('Unsupported schema version ' . $version);
+        }
+
+        $this->config = $config;
+    }
+
+    private function configureVersionZero(stdClass $config)
+    {
+        $config->mode = 'ctr';
+        $config->options = 0;
+        $config->hmac->includesHeader = false;
+        $config->hmac->algorithm = 'sha1';
+        $config->hmac->includesPadding = true;
+        $config->truncatesMultibytePasswords = true;
+    }
 }

+ 93 - 116
lib/RNCryptor/Decryptor.php

@@ -1,124 +1,101 @@
 <?php
-namespace RNCryptor;
+namespace RNCryptor\RNCryptor;
+
+use stdClass;
 
 /**
  * RNDecryptor for PHP
- * 
+ *
  * Decrypt data interchangeably with Rob Napier's Objective-C implementation
  * of RNCryptor
  */
-class Decryptor extends Cryptor {
-
-	/**
-	 * Decrypt RNCryptor-encrypted data
-	 *
-	 * @param string $base64EncryptedData Encrypted, Base64-encoded text
-	 * @param string $password Password the text was encoded with
-	 * @throws Exception If the detected version is unsupported
-	 * @return string|false Decrypted string, or false if decryption failed
-	 */
-	public function decrypt($encryptedBase64Data, $password) {
-
-		$components = $this->_unpackEncryptedBase64Data($encryptedBase64Data);
-
-		if (!$this->_hmacIsValid($components, $password)) {
-			return false;
-		}
-
-		$key = $this->_generateKey($components->headers->encSalt, $password);
-		$plaintext = null;
-		switch ($this->_settings->mode) {
-			case 'ctr':
-				$plaintext = $this->_aesCtrLittleEndianCrypt($components->ciphertext, $key, $components->headers->iv);
-				break;
-
-			case 'cbc':
-                $plaintext = $this->_decrypt_internal($key, $components->ciphertext, 'cbc', $components->headers->iv);
-				break;
-		}
-
-		return $plaintext;
-	}
-
-	public function decryptWithArbitraryKeys($encryptedBase64Data, $encKey, $hmacKey) {
-		$components = $this->_unpackEncryptedBase64Data($encryptedBase64Data, false);
-		if (!$this->_hmacIsValid($components, $hmacKey, false)) {
-			return false;
-		}
-		$plaintext = null;
-		switch ($this->_settings->mode) {
-			case 'ctr':
-				$plaintext = $this->_aesCtrLittleEndianCrypt($components->ciphertext, $encKey, $components->headers->iv);
-				break;
-			case 'cbc':
-				$paddedPlaintext = mcrypt_decrypt($this->_settings->algorithm, $encKey, $components->ciphertext, 'cbc', $components->headers->iv);
-				$plaintext = $this->_stripPKCS7Padding($paddedPlaintext);
-				break;
-		}
-		return $plaintext;
-	}
-
-	private function _unpackEncryptedBase64Data($encryptedBase64Data, $isPasswordBased = true) {
-
-		$binaryData = base64_decode($encryptedBase64Data);
-
-		$components = new \stdClass();
-		$components->headers = $this->_parseHeaders($binaryData, $isPasswordBased);
-
-		$components->hmac = substr($binaryData, - $this->_settings->hmac->length);
-
-		$headerLength = $components->headers->length;
-
-		$components->ciphertext = substr($binaryData, $headerLength, strlen($binaryData) - $headerLength - strlen($components->hmac));
-
-		return $components;
-	}
-
-	private function _parseHeaders($binData, $isPasswordBased = true) {
-
-		$offset = 0;
-
-		$versionChr = $binData[0];
-		$offset += strlen($versionChr);
-
-		$this->_configureSettings(ord($versionChr));
-
-		$optionsChr = $binData[1];
-		$offset += strlen($optionsChr);
-
-		$encSalt = null;
-		$hmacSalt = null;
-		if($isPasswordBased) {
-			$encSalt = substr($binData, $offset, $this->_settings->saltLength);
-			$offset += strlen($encSalt);
-
-			$hmacSalt = substr($binData, $offset, $this->_settings->saltLength);
-			$offset += strlen($hmacSalt);
-		}
-
-		$iv = substr($binData, $offset, $this->_settings->ivLength);
-		$offset += strlen($iv);
-
-		$headers = (object)array(
-			'version' => $versionChr,
-			'options' => $optionsChr,
-			'encSalt' => $encSalt,
-			'hmacSalt' => $hmacSalt,
-			'iv' => $iv,
-			'length' => $offset
-		);
-
-		return $headers;
-	}
-
-	private function _stripPKCS7Padding($plaintext) {
-		$padLength = ord($plaintext[strlen($plaintext)-1]);
-		return substr($plaintext, 0, strlen($plaintext) - $padLength);
-	}
-
-	private function _hmacIsValid($components, $password) {
-        $hmacKey = $this->_generateKey($components->headers->hmacSalt, $password);
-
-        return hash_equals($components->hmac, $this->_generateHmac($components, $hmacKey));
-	}
+class Decryptor extends Cryptor
+{
+    /**
+     * Decrypt RNCryptor-encrypted data
+     *
+     * @param string $base64EncryptedData Encrypted, Base64-encoded text
+     * @param string $password Password the text was encoded with
+     * @throws Exception If the detected version is unsupported
+     * @return string|false Decrypted string, or false if decryption failed
+     */
+    public function decrypt($encryptedBase64Data, $password)
+    {
+        $components = $this->unpackEncryptedBase64Data($encryptedBase64Data);
+
+        if (!$this->hmacIsValid($components, $password)) {
+            return false;
+        }
+
+        $key = $this->makeKey($components->headers->encSalt, $password);
+        if ($this->config->mode == 'ctr') {
+            return $this->aesCtrLittleEndianCrypt($components->ciphertext, $key, $components->headers->iv);
+        }
+
+        $iv = (string)$components->headers->iv;
+        $method = $this->config->algorithm . 'cbc';
+
+        return openssl_decrypt($components->ciphertext, $method, $key, OPENSSL_RAW_DATA, (string)$iv);
+    }
+
+    private function unpackEncryptedBase64Data($encryptedBase64Data, $isPasswordBased = true)
+    {
+        $binaryData = base64_decode($encryptedBase64Data);
+
+        $components = new stdClass;
+        $components->headers = $this->parseHeaders($binaryData, $isPasswordBased);
+
+        $components->hmac = substr($binaryData, -$this->config->hmac->length);
+
+        $offset = $components->headers->length;
+        $length = strlen($binaryData) - $offset - strlen($components->hmac);
+
+        $components->ciphertext = substr($binaryData, $offset, $length);
+
+        return $components;
+    }
+
+    private function parseHeaders($binData, $isPasswordBased = true)
+    {
+        $offset = 0;
+
+        $versionChr = $binData[0];
+        $offset += strlen($versionChr);
+
+        $this->configure(ord($versionChr));
+
+        $optionsChr = $binData[1];
+        $offset += strlen($optionsChr);
+
+        $encSalt = null;
+        $hmacSalt = null;
+        if ($isPasswordBased) {
+            $encSalt = substr($binData, $offset, $this->config->saltLength);
+            $offset += strlen($encSalt);
+
+            $hmacSalt = substr($binData, $offset, $this->config->saltLength);
+            $offset += strlen($hmacSalt);
+        }
+
+        $iv = substr($binData, $offset, $this->config->ivLength);
+        $offset += strlen($iv);
+
+        $headers = (object)[
+            'version' => $versionChr,
+            'options' => $optionsChr,
+            'encSalt' => $encSalt,
+            'hmacSalt' => $hmacSalt,
+            'iv' => $iv,
+            'length' => $offset
+        ];
+
+        return $headers;
+    }
+
+    private function hmacIsValid($components, $password)
+    {
+        $hmacKey = $this->makeKey($components->headers->hmacSalt, $password);
+
+        return hash_equals($components->hmac, $this->makeHmac($components, $hmacKey));
+    }
 }

+ 105 - 101
lib/RNCryptor/Encryptor.php

@@ -1,110 +1,114 @@
 <?php
-namespace RNCryptor;
+namespace RNCryptor\RNCryptor;
+
+use stdClass;
 
 /**
  * RNEncryptor for PHP
- * 
+ *
  * Encrypt data interchangeably with Rob Napier's Objective-C implementation
  * of RNCryptor
  */
-class Encryptor extends Cryptor {
-
-	/**
-	 * Encrypt plaintext using RNCryptor's algorithm
-	 * 
-	 * @param string $plaintext Text to be encrypted
-	 * @param string $password Password to use
-	 * @param int $version (Optional) RNCryptor schema version to use.
-	 * @throws \Exception If the provided version (if any) is unsupported
-	 * @return string Encrypted, Base64-encoded string
-	 */
-	public function encrypt($plaintext, $password, $version = Cryptor::DEFAULT_SCHEMA_VERSION) {
-
-		$this->_configureSettings($version);
-
-		$components = $this->_generateInitializedComponents($version);
-		$components->headers->encSalt = $this->_generateSalt();
-		$components->headers->hmacSalt = $this->_generateSalt();
-		$components->headers->iv = $this->_generateIv($this->_settings->ivLength);
-
-		$encKey = $this->_generateKey($components->headers->encSalt, $password);
-		$hmacKey = $this->_generateKey($components->headers->hmacSalt, $password);
-
-		return $this->_encrypt($plaintext, $components, $encKey, $hmacKey);
-	}
-
-	public function encryptWithArbitrarySalts($plaintext, $password, $encSalt, $hmacSalt, $iv, $version = Cryptor::DEFAULT_SCHEMA_VERSION) {
-	
-		$this->_configureSettings($version);
-
-		$components = $this->_generateInitializedComponents($version);
-		$components->headers->encSalt = $encSalt;
-		$components->headers->hmacSalt = $hmacSalt;
-		$components->headers->iv = $iv;
-
-		$encKey = $this->_generateKey($components->headers->encSalt, $password);
-		$hmacKey = $this->_generateKey($components->headers->hmacSalt, $password);
-
-		return $this->_encrypt($plaintext, $components, $encKey, $hmacKey);
-	}
-
-	public function encryptWithArbitraryKeys($plaintext, $encKey, $hmacKey, $iv, $version = Cryptor::DEFAULT_SCHEMA_VERSION) {
-
-		$this->_configureSettings($version);
-
-		$this->_settings->options = 0;
-
-		$components = $this->_generateInitializedComponents($version);
-		$components->headers->iv = $iv;
-
-		return $this->_encrypt($plaintext, $components, $encKey, $hmacKey);
-	}
-
-	private function _generateInitializedComponents($version) {
-
-		$components = new \stdClass();
-		$components->headers = new \stdClass();
-		$components->headers->version = chr($version);
-		$components->headers->options = chr($this->_settings->options);
-
-		return $components;
-	}
-
-	private function _encrypt($plaintext, \stdClass $components, $encKey, $hmacKey) {
-	
-		switch ($this->_settings->mode) {
-			case 'ctr':
-				$components->ciphertext = $this->_aesCtrLittleEndianCrypt($plaintext, $encKey, $components->headers->iv);
-				break;
-	
-			case 'cbc':
-                $components->ciphertext = $this->_encrypt_internal($encKey, $plaintext, 'cbc', $components->headers->iv);
-				break;
-		}
-
-		$binaryData = ''
-				. $components->headers->version
-				. $components->headers->options
-				. (isset($components->headers->encSalt) ? $components->headers->encSalt : '')
-				. (isset($components->headers->hmacSalt) ? $components->headers->hmacSalt : '')
-				. $components->headers->iv
-				. $components->ciphertext;
-	
-		$hmac = $this->_generateHmac($components, $hmacKey);
-	
-		return base64_encode($binaryData . $hmac);
-	}
-
-	private function _addPKCS7Padding($plaintext, $blockSize) {
-		$padSize = $blockSize - (strlen($plaintext) % $blockSize);
-		return $plaintext . str_repeat(chr($padSize), $padSize);
-	}
-
-	private function _generateSalt() {
-		return $this->_generateIv($this->_settings->saltLength);
-	}
-
-	private function _generateIv($blockSize) {
+class Encryptor extends Cryptor
+{
+    /**
+     * Encrypt plaintext using RNCryptor's algorithm
+     *
+     * @param string $plaintext Text to be encrypted
+     * @param string $password Password to use
+     * @param int $version (Optional) RNCryptor schema version to use.
+     * @throws \Exception If the provided version (if any) is unsupported
+     * @return string Encrypted, Base64-encoded string
+     */
+    public function encrypt($plaintext, $password, $version = Cryptor::DEFAULT_SCHEMA_VERSION)
+    {
+        $this->configure($version);
+
+        $components = $this->makeComponents($version);
+        $components->headers->encSalt = $this->makeSalt();
+        $components->headers->hmacSalt = $this->makeSalt();
+        $components->headers->iv = $this->makeIv($this->config->ivLength);
+
+        $encKey = $this->makeKey($components->headers->encSalt, $password);
+        $hmacKey = $this->makeKey($components->headers->hmacSalt, $password);
+
+        return $this->encryptFromComponents($plaintext, $components, $encKey, $hmacKey);
+    }
+
+    public function encryptWithArbitrarySalts(
+        $plaintext,
+        $password,
+        $encSalt,
+        $hmacSalt,
+        $iv,
+        $version = Cryptor::DEFAULT_SCHEMA_VERSION
+    ) {
+        $this->configure($version);
+
+        $components = $this->makeComponents($version);
+        $components->headers->encSalt = $encSalt;
+        $components->headers->hmacSalt = $hmacSalt;
+        $components->headers->iv = $iv;
+
+        $encKey = $this->makeKey($components->headers->encSalt, $password);
+        $hmacKey = $this->makeKey($components->headers->hmacSalt, $password);
+
+        return $this->encryptFromComponents($plaintext, $components, $encKey, $hmacKey);
+    }
+
+    public function encryptWithArbitraryKeys(
+        $plaintext,
+        $encKey,
+        $hmacKey,
+        $iv,
+        $version = Cryptor::DEFAULT_SCHEMA_VERSION
+    ) {
+        $this->configure($version);
+
+        $this->config->options = 0;
+
+        $components = $this->makeComponents($version);
+        $components->headers->iv = $iv;
+
+        return $this->encryptFromComponents($plaintext, $components, $encKey, $hmacKey);
+    }
+
+    private function makeComponents($version)
+    {
+        $components = new stdClass;
+        $components->headers = new stdClass;
+        $components->headers->version = chr($version);
+        $components->headers->options = chr($this->config->options);
+
+        return $components;
+    }
+
+    private function encryptFromComponents($plaintext, stdClass $components, $encKey, $hmacKey)
+    {
+        $iv = $components->headers->iv;
+        if ($this->config->mode == 'ctr') {
+            $components->ciphertext = $this->aesCtrLittleEndianCrypt($plaintext, $encKey, $iv);
+        } else {
+            $components->ciphertext = $this->encryptInternal($encKey, $plaintext, 'cbc', $iv);
+        }
+
+        return base64_encode(''
+            . $components->headers->version
+            . $components->headers->options
+            . ($components->headers->encSalt ?? '')
+            . ($components->headers->hmacSalt ?? '')
+            . $components->headers->iv
+            . $components->ciphertext
+            . $this->makeHmac($components, $hmacKey));
+    }
+
+    private function makeSalt()
+    {
+        return $this->makeIv($this->config->saltLength);
+    }
+
+    private function makeIv($blockSize)
+    {
         return openssl_random_pseudo_bytes($blockSize);
-	}
+    }
 }

+ 146 - 133
tests/RNCryptor/CryptorTest.php

@@ -1,148 +1,161 @@
 <?php
-namespace RNCryptor;
+namespace Tests\RNCryptor;
 
 use PHPUnit\Framework\TestCase;
+use RNCryptor\RNCryptor\Decryptor;
+use RNCryptor\RNCryptor\Encryptor;
 
-class CryptorTest extends TestCase {
+class CryptorTest extends TestCase
+{
+    // relative to __DIR__
+    const TEXT_FILENAME = 'lorem-ipsum.txt';
 
-	// relative to __DIR__
-	const TEXT_FILENAME = 'lorem-ipsum.txt';
+    const SAMPLE_PLAINTEXT = 'What\'s your name?  My name is Tilgath Pilesar.  Why are you crying?';
+    const SAMPLE_PASSWORD = 'do-not-write-this-down';
+    
+    const SAMPLE_PLAINTEXT_V2_BLOCKSIZE = 'Lorem ipsum dolor sit amet, cons';
 
-	const SAMPLE_PLAINTEXT = 'What\'s your name?  My name is Tilgath Pilesar.  Why are you crying?';
-	const SAMPLE_PASSWORD = 'do-not-write-this-down';
-	
-	const SAMPLE_PLAINTEXT_V2_BLOCKSIZE = 'Lorem ipsum dolor sit amet, cons';
-
-    public static function main() {
+    public static function main()
+    {
         $suite  = new PHPUnit_Framework_TestSuite(get_called_class());
-        $result = PHPUnit_TextUI_TestRunner::run($suite);
+        PHPUnit_TextUI_TestRunner::run($suite);
+    }
+
+    public function testCanDecryptSelfEncryptedDefaultVersion()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD);
+        
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
+    }
+
+    public function testCanDecryptSelfEncryptedStringEqualToBlockSizeMultiple()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT_V2_BLOCKSIZE, self::SAMPLE_PASSWORD);
+    
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT_V2_BLOCKSIZE, $decrypted);
+    }
+
+    public function testCanDecryptSelfEncryptedVersion0()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 0);
+        
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
+    }
+
+    public function testCanDecryptSelfEncryptedVersion1()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 1);
+        
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
+    }
+    
+    public function testCanDecryptSelfEncryptedVersion2()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 2);
+    
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
     }
 
-  	public function testCanDecryptSelfEncryptedDefaultVersion() {
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD);
-  		
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-  	}
-
-  	public function testCanDecryptSelfEncryptedStringEqualToBlockSizeMultiple() {
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT_V2_BLOCKSIZE, self::SAMPLE_PASSWORD);
-  	
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT_V2_BLOCKSIZE, $decrypted);
-  	}
-
-  	public function testCanDecryptSelfEncryptedVersion0() {
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 0);
-  		
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-  	}
-
-  	public function testCanDecryptSelfEncryptedVersion1() {
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 1);
-  		
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-  	}
-  	
-  	public function testCanDecryptSelfEncryptedVersion2() {
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 2);
-  	
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-  	}
-
-  	public function testCanDecryptLongText() {
-
-  		$text = file_get_contents(__DIR__ . '/_files/lorem-ipsum.txt');
-  	
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt($text, self::SAMPLE_PASSWORD);
-  	
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
-  		$this->assertEquals($text, $decrypted);
-  	}
-
-  	public function testVersion1TruncatesMultibytePasswords() {
-  		$password1 = '中文密码';
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, $password1, 1);
-
-  		// Yikes, it's truncated! So with an all-multibyte password
-  		// like above, we can replace the last half of the string
-  		// with whatver we want, and decryption will still work.
-  		$password2 = '中文中文';
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, $password2);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-  	
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, $password1);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-  	}
-
-  	public function testVersion2TruncatesMultibytePasswords() {
-  		$password1 = '中文密码';
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, $password1, 2);
-
-  		// Yikes, it's truncated! So with an all-multibyte password
-  		// like above, we can replace the last half of the string
-  		// with whatver we want, and decryption will still work.
-  		$password2 = '中文中文';
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, $password2);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, $password1);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-  	}
-
-  	public function testVersion3AcceptsMultibytePasswords() {
-  		$password1 = '中文密码';
-  		$encryptor = new Encryptor();
-  		$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, $password1, 3);
-
-  		$password2 = '中文中文';
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, $password2);
-  		$this->assertFalse($decrypted);
-
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt($encrypted, $password1);
-  		$this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
-  	}
+    public function testCanDecryptLongText()
+    {
+        $text = file_get_contents(__DIR__ . '/_files/lorem-ipsum.txt');
+    
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt($text, self::SAMPLE_PASSWORD);
+    
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
+        $this->assertEquals($text, $decrypted);
+    }
+
+    public function testVersion1TruncatesMultibytePasswords()
+    {
+        $password1 = '中文密码';
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, $password1, 1);
+
+        // Yikes, it's truncated! So with an all-multibyte password
+        // like above, we can replace the last half of the string
+        // with whatver we want, and decryption will still work.
+        $password2 = '中文中文';
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, $password2);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
+    
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, $password1);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
+    }
+
+    public function testVersion2TruncatesMultibytePasswords()
+    {
+        $password1 = '中文密码';
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, $password1, 2);
+
+        // Yikes, it's truncated! So with an all-multibyte password
+        // like above, we can replace the last half of the string
+        // with whatver we want, and decryption will still work.
+        $password2 = '中文中文';
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, $password2);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
+
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, $password1);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
+    }
+
+    public function testVersion3AcceptsMultibytePasswords()
+    {
+        $password1 = '中文密码';
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, $password1, 3);
+
+        $password2 = '中文中文';
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, $password2);
+        $this->assertFalse($decrypted);
+
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt($encrypted, $password1);
+        $this->assertEquals(self::SAMPLE_PLAINTEXT, $decrypted);
+    }
 
     /**
      * @expectedException \Exception
      */
-  	public function testCannotUseWithUnsupportedSchemaVersions() {
-  		$fakeSchemaNumber = 57;
-  		$encrypted = $this->_generateEncryptedStringWithUnsupportedSchemaNumber($fakeSchemaNumber);
-  		$decryptor = new Decryptor();
-  		$decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
-  	}
-
-  	private function _generateEncryptedStringWithUnsupportedSchemaNumber($fakeSchemaNumber) {
-  		$encryptor = new Encryptor();
-  		$plaintext = 'The price of ice is nice for mice';
-  		$encrypted = $encryptor->encrypt($plaintext, self::SAMPLE_PASSWORD);
-
-  		$encryptedBinary = base64_decode($encrypted);
+    public function testCannotUseWithUnsupportedSchemaVersions()
+    {
+        $fakeSchemaNumber = 57;
+        $encrypted = $this->generateEncryptedStringWithUnsupportedSchemaNumber($fakeSchemaNumber);
+        $decryptor = new Decryptor;
+        $decryptor->decrypt($encrypted, self::SAMPLE_PASSWORD);
+    }
+
+    private function generateEncryptedStringWithUnsupportedSchemaNumber($fakeSchemaNumber)
+    {
+        $encryptor = new Encryptor;
+        $plaintext = 'The price of ice is nice for mice';
+        $encrypted = $encryptor->encrypt($plaintext, self::SAMPLE_PASSWORD);
+
+        $encryptedBinary = base64_decode($encrypted);
         $encryptedBinary = chr($fakeSchemaNumber) . substr($encryptedBinary, 1);
-  		return base64_encode($encryptedBinary);
-  	}
+        return base64_encode($encryptedBinary);
+    }
 }

+ 125 - 88
tests/RNCryptor/DecryptorTest.php

@@ -1,106 +1,143 @@
 <?php
-namespace RNCryptor;
+namespace Tests\RNCryptor;
 
 use PHPUnit\Framework\TestCase;
+use RNCryptor\RNCryptor\Decryptor;
 
-class DecryptorTest extends TestCase {
+class DecryptorTest extends TestCase
+{
 
-	const IOS_PASSWORD = 'mypassword123$!';
+    const IOS_PASSWORD = 'mypassword123$!';
 
-	const PLAINTEXT_V0_LESS_THAN_ONE_BLOCK = 'Monkey';
-	const IOS_ENCRYPTED_V0_LESS_THAN_ONE_BLOCK = 'AACoGb/5NAItZ9gY0YkCXK0Q7d+1p2mNyFFKIDldCA5QRqX5i9MNpezRS7CDX8jUDKGtIlZU6d8CZQeJAAAAAAAAAAAAAAAA';
-	
-	const PLAINTEXT_V0_EXACTLY_ONE_BLOCK = 'O happy day now.';
-	const IOS_ENCRYPTED_V0_EXACTLY_ONE_BLOCK = 'AADsM/JbTInOMSm0epc/7MqQ1Ol2Fu/ySnQ0FknhJeTD6GpZo+SF8JDloHN82yZIHrOcJ3vZuXmrCUt3AysLYg6Vpu4KDwAAAAAAAAAAAAAAAA==';
-	
-	const PLAINTEXT_V0_EXACTLY_TWO_BLOCKS = 'Earth is round, sun is round too';
-	const IOS_ENCRYPTED_V0_EXACTLY_TWO_BLOCKS = 'AAApp4OoYpg4Fz+WSZDbcf5KPJasOkhdCnptrmwVkt58BZi/lnTWoIOf2IhIZhHsvTKYYEJsds6bFL/nZC/GtENusHWFyEw1IdtQ7KFSp8XZEhiAT88AAAAAAAAAAAAAAAA=';
-	
-	const PLAINTEXT_V0_NON_BLOCK_INTERVAL = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do...';
-	const IOS_ENCRYPTED_V0_NON_BLOCK_INTERVAL = 'AADu55As8qH9KsSR17p1akydMUlbHrsHudMOr/yTj4olfQedJPTZg8hK4ua99zNkj3Nw7Hle1f1onHclWIYoLkWtMVk4Cp96CcxRhaWbBZqAVvTabtVruxcAi+GEB2K4rrmyARxB2QJH9tfz2yTFoFNMln+xOCUm0wAAAAAAAAAAAAAAAA==';
-	
-	const PLAINTEXT_V1_EXACTLY_ONE_BLOCK = 'Lorem ipsum dolor sit amet, cons';
-	const IOS_ENCRYPTED_V1_EXACTLY_ONE_BLOCK = 'AQEjdvTrgCAo8UMn9omCd30um3iMfq/Swiglr5I/wAESEuHBcdbtpbqpUliyDs6NyI83SQGzV9wpAdW8EYBzGdJ1AcE/nld27XX9jPF4Fj+X++Ws4EL2gEoJYO1fGuX3+hUFhIWaPCzxg/HvLMTDVq4k';
-	
-	const PLAINTEXT_V1_NON_BLOCK_INTERVAL = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do...';
-	const IOS_ENCRYPTED_V1_NON_BLOCK_INTERVAL = 'AQE9u3aB1APkWDRHcfy1cvD3kwwoXUw+8JhtCkZ3xDkSQghIyFoqLgazX3cXBxv3Mj75sSofHoDI35KaFTdXovY3HQYAaQmMdPNvSRVGvlptkyr5LSBMUA3/Uj7lmhnaf515pN8pUbcbOV8RP+oWhXX4iKN009mrcMaX2j1KQz2JfFj8bfpbu9BOtj+1NotIe14=';
-	
-	const PLAINTEXT_V2_EXACTLY_ONE_BLOCK = 'Lorem ipsum dolor sit amet, cons';
-	const IOS_ENCRYPTED_V2_EXACTLY_ONE_BLOCK = 'AgEjDKHOcviYJbHBiZ4l0sku8Dd+0EZIUEz69uTtQI/yJorbiCu3mxpbTVrM6Kj4/vywmOdXdwSR0ov2S/oJ1rVtA8gJ2ulKyrYOOySfDS0/YioWKe21zJMfizK8PHveyjBoKmIJdPhT5/caF3l/+JCs';
-	
-	const PLAINTEXT_V2_NON_BLOCK_INTERVAL = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do...';
-	const IOS_ENCRYPTED_V2_NON_BLOCK_INTERVAL = 'AgG8X+ixN6HN9zFnuK1NMJAPntIuC0+WPsmFhGL314zLuq1T9xWDHYzpnzW8EqDz81Amj36+EqrjazQ1gO9ao6bpMwUKdT2xY4ZUrhtCQm3LD2okbEIGjj5dtMJtB3i759WdnmNf8K0ULDWNzNQHPzdNDcEE2BPh+2kRaqVzWyBOzJppJoD5n+WdglS7BEBU+4U=';
+    const PLAINTEXT_V0_LESS_THAN_ONE_BLOCK = 'Monkey';
+    const IOS_ENCRYPTED_V0_LESS_THAN_ONE_BLOCK
+        = 'AACoGb/5NAItZ9gY0YkCXK0Q7d+1p2mNyFFKIDldCA5QRqX5i9MNpezRS7CDX8jUDKGtIlZU6d8CZQeJAAAAAAAAAAAAAAAA';
+    
+    const PLAINTEXT_V0_EXACTLY_ONE_BLOCK = 'O happy day now.';
+    const IOS_ENCRYPTED_V0_EXACTLY_ONE_BLOCK
+        = 'AADsM/JbTInOMSm0epc/7MqQ1Ol2Fu/ySnQ0FknhJeTD6GpZo+SF8JDloHN82yZIHrOcJ3vZuXmrCUt3AysLYg6Vpu4KDwAAAAAAAAAAAAAA'
+        . 'AA==';
+    
+    const PLAINTEXT_V0_EXACTLY_TWO_BLOCKS = 'Earth is round, sun is round too';
+    const IOS_ENCRYPTED_V0_EXACTLY_TWO_BLOCKS
+        = 'AAApp4OoYpg4Fz+WSZDbcf5KPJasOkhdCnptrmwVkt58BZi/lnTWoIOf2IhIZhHsvTKYYEJsds6bFL/nZC/GtENusHWFyEw1IdtQ7KFSp8XZ'
+        . 'EhiAT88AAAAAAAAAAAAAAAA=';
+    
+    const PLAINTEXT_V0_NON_BLOCK_INTERVAL = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do...';
+    const IOS_ENCRYPTED_V0_NON_BLOCK_INTERVAL
+        = 'AADu55As8qH9KsSR17p1akydMUlbHrsHudMOr/yTj4olfQedJPTZg8hK4ua99zNkj3Nw7Hle1f1onHclWIYoLkWtMVk4Cp96CcxRhaWbBZqA'
+        . 'VvTabtVruxcAi+GEB2K4rrmyARxB2QJH9tfz2yTFoFNMln+xOCUm0wAAAAAAAAAAAAAAAA==';
+    
+    const PLAINTEXT_V1_EXACTLY_ONE_BLOCK = 'Lorem ipsum dolor sit amet, cons';
+    const IOS_ENCRYPTED_V1_EXACTLY_ONE_BLOCK
+        = 'AQEjdvTrgCAo8UMn9omCd30um3iMfq/Swiglr5I/wAESEuHBcdbtpbqpUliyDs6NyI83SQGzV9wpAdW8EYBzGdJ1AcE/nld27XX9jPF4Fj+X'
+        . '++Ws4EL2gEoJYO1fGuX3+hUFhIWaPCzxg/HvLMTDVq4k';
+    
+    const PLAINTEXT_V1_NON_BLOCK_INTERVAL = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do...';
+    const IOS_ENCRYPTED_V1_NON_BLOCK_INTERVAL
+        = 'AQE9u3aB1APkWDRHcfy1cvD3kwwoXUw+8JhtCkZ3xDkSQghIyFoqLgazX3cXBxv3Mj75sSofHoDI35KaFTdXovY3HQYAaQmMdPNvSRVGvlpt'
+        . 'kyr5LSBMUA3/Uj7lmhnaf515pN8pUbcbOV8RP+oWhXX4iKN009mrcMaX2j1KQz2JfFj8bfpbu9BOtj+1NotIe14=';
+    
+    const PLAINTEXT_V2_EXACTLY_ONE_BLOCK = 'Lorem ipsum dolor sit amet, cons';
+    const IOS_ENCRYPTED_V2_EXACTLY_ONE_BLOCK
+        = 'AgEjDKHOcviYJbHBiZ4l0sku8Dd+0EZIUEz69uTtQI/yJorbiCu3mxpbTVrM6Kj4/vywmOdXdwSR0ov2S/oJ1rVtA8gJ2ulKyrYOOySfDS0/'
+        . 'YioWKe21zJMfizK8PHveyjBoKmIJdPhT5/caF3l/+JCs';
+    
+    const PLAINTEXT_V2_NON_BLOCK_INTERVAL = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do...';
+    const IOS_ENCRYPTED_V2_NON_BLOCK_INTERVAL
+        = 'AgG8X+ixN6HN9zFnuK1NMJAPntIuC0+WPsmFhGL314zLuq1T9xWDHYzpnzW8EqDz81Amj36+EqrjazQ1gO9ao6bpMwUKdT2xY4ZUrhtCQm3L'
+        . 'D2okbEIGjj5dtMJtB3i759WdnmNf8K0ULDWNzNQHPzdNDcEE2BPh+2kRaqVzWyBOzJppJoD5n+WdglS7BEBU+4U=';
 
-  	public function testCanDecryptIosEncryptedVersion0WithPlaintextLengthLessThanOneBlock() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V0_LESS_THAN_ONE_BLOCK, self::IOS_PASSWORD);
-  		$this->assertEquals(self::PLAINTEXT_V0_LESS_THAN_ONE_BLOCK, $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion0WithPlaintextLengthLessThanOneBlock()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V0_LESS_THAN_ONE_BLOCK, self::IOS_PASSWORD);
+        $this->assertEquals(self::PLAINTEXT_V0_LESS_THAN_ONE_BLOCK, $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion0WithPlaintextReallyLong() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(file_get_contents(__DIR__ . '/_files/lorem-ipsum-encrypted-base64-schema0.txt'), self::IOS_PASSWORD);
-  		$this->assertEquals(file_get_contents(__DIR__ . '/_files/lorem-ipsum.txt'), $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion0WithPlaintextReallyLong()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(
+            file_get_contents(__DIR__ . '/_files/lorem-ipsum-encrypted-base64-schema0.txt'),
+            self::IOS_PASSWORD
+        );
+        $this->assertEquals(file_get_contents(__DIR__ . '/_files/lorem-ipsum.txt'), $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion0WithPlaintextLengthExactlyOneBlock() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V0_EXACTLY_ONE_BLOCK, self::IOS_PASSWORD);
-  		$this->assertEquals(self::PLAINTEXT_V0_EXACTLY_ONE_BLOCK, $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion0WithPlaintextLengthExactlyOneBlock()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V0_EXACTLY_ONE_BLOCK, self::IOS_PASSWORD);
+        $this->assertEquals(self::PLAINTEXT_V0_EXACTLY_ONE_BLOCK, $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion0WithPlaintextLengthExactlyTwoBlocks() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V0_EXACTLY_TWO_BLOCKS, self::IOS_PASSWORD);
-  		$this->assertEquals(self::PLAINTEXT_V0_EXACTLY_TWO_BLOCKS, $decrypted);
-  	}
-  	
-  	public function testCanDecryptIosEncryptedVersion0WithPlaintextLengthNotOnBlockInterval() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V0_NON_BLOCK_INTERVAL, self::IOS_PASSWORD);
-  		$this->assertEquals(self::PLAINTEXT_V0_NON_BLOCK_INTERVAL, $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion0WithPlaintextLengthExactlyTwoBlocks()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V0_EXACTLY_TWO_BLOCKS, self::IOS_PASSWORD);
+        $this->assertEquals(self::PLAINTEXT_V0_EXACTLY_TWO_BLOCKS, $decrypted);
+    }
+    
+    public function testCanDecryptIosEncryptedVersion0WithPlaintextLengthNotOnBlockInterval()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V0_NON_BLOCK_INTERVAL, self::IOS_PASSWORD);
+        $this->assertEquals(self::PLAINTEXT_V0_NON_BLOCK_INTERVAL, $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion1WithPlaintextReallyLong() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(file_get_contents(__DIR__ . '/_files/lorem-ipsum-encrypted-base64-schema1.txt'), self::IOS_PASSWORD);
-  		$this->assertEquals(file_get_contents(__DIR__ . '/_files/lorem-ipsum.txt'), $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion1WithPlaintextReallyLong()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(
+            file_get_contents(__DIR__ . '/_files/lorem-ipsum-encrypted-base64-schema1.txt'),
+            self::IOS_PASSWORD
+        );
+        $this->assertEquals(file_get_contents(__DIR__ . '/_files/lorem-ipsum.txt'), $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion1WithPlaintextLengthExactlyOneBlock() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V1_EXACTLY_ONE_BLOCK, self::IOS_PASSWORD);
-  		$this->assertEquals(self::PLAINTEXT_V1_EXACTLY_ONE_BLOCK, $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion1WithPlaintextLengthExactlyOneBlock()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V1_EXACTLY_ONE_BLOCK, self::IOS_PASSWORD);
+        $this->assertEquals(self::PLAINTEXT_V1_EXACTLY_ONE_BLOCK, $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion1WithPlaintextLengthNotOnBlockInterval() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V1_NON_BLOCK_INTERVAL, self::IOS_PASSWORD);
-  		$this->assertEquals(self::PLAINTEXT_V1_NON_BLOCK_INTERVAL, $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion1WithPlaintextLengthNotOnBlockInterval()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V1_NON_BLOCK_INTERVAL, self::IOS_PASSWORD);
+        $this->assertEquals(self::PLAINTEXT_V1_NON_BLOCK_INTERVAL, $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion2WithPlaintextReallyLong() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(file_get_contents(__DIR__ . '/_files/lorem-ipsum-encrypted-base64-schema2.txt'), self::IOS_PASSWORD);
-  		$this->assertEquals(file_get_contents(__DIR__ . '/_files/lorem-ipsum.txt'), $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion2WithPlaintextReallyLong()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(
+            file_get_contents(__DIR__ . '/_files/lorem-ipsum-encrypted-base64-schema2.txt'),
+            self::IOS_PASSWORD
+        );
+        $this->assertEquals(file_get_contents(__DIR__ . '/_files/lorem-ipsum.txt'), $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion2WithPlaintextLengthExactlyOneBlock() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V2_EXACTLY_ONE_BLOCK, self::IOS_PASSWORD);
-  		$this->assertEquals(self::PLAINTEXT_V2_EXACTLY_ONE_BLOCK, $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion2WithPlaintextLengthExactlyOneBlock()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V2_EXACTLY_ONE_BLOCK, self::IOS_PASSWORD);
+        $this->assertEquals(self::PLAINTEXT_V2_EXACTLY_ONE_BLOCK, $decrypted);
+    }
 
-  	public function testCanDecryptIosEncryptedVersion2WithPlaintextLengthNotOnBlockInterval() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V2_NON_BLOCK_INTERVAL, self::IOS_PASSWORD);
-  		$this->assertEquals(self::PLAINTEXT_V2_NON_BLOCK_INTERVAL, $decrypted);
-  	}
+    public function testCanDecryptIosEncryptedVersion2WithPlaintextLengthNotOnBlockInterval()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V2_NON_BLOCK_INTERVAL, self::IOS_PASSWORD);
+        $this->assertEquals(self::PLAINTEXT_V2_NON_BLOCK_INTERVAL, $decrypted);
+    }
 
-  	public function testDecryptingWithBadPasswordFails() {
-  		$decryptor = new Decryptor();
-  		$decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V2_NON_BLOCK_INTERVAL, 'bad-password');
-  		$this->assertEquals(false, $decrypted);
-  	}
-  	
+    public function testDecryptingWithBadPasswordFails()
+    {
+        $decryptor = new Decryptor;
+        $decrypted = $decryptor->decrypt(self::IOS_ENCRYPTED_V2_NON_BLOCK_INTERVAL, 'bad-password');
+        $this->assertEquals(false, $decrypted);
+    }
 }

+ 44 - 36
tests/RNCryptor/EncryptorTest.php

@@ -1,56 +1,64 @@
 <?php
-namespace RNCryptor;
+namespace Tests\RNCryptor;
 
 use PHPUnit\Framework\TestCase;
+use RNCryptor\RNCryptor\Encryptor;
 
-class EncryptorTest extends TestCase {
+class EncryptorTest extends TestCase
+{
 
-	const SAMPLE_PLAINTEXT = 'Hello, how are you today?  I am doing fine.';
-	const SAMPLE_PASSWORD = 'keep-out-123';
+    const SAMPLE_PLAINTEXT = 'Hello, how are you today?  I am doing fine.';
+    const SAMPLE_PASSWORD = 'keep-out-123';
 
-    public function testCanEncryptWithDefaultVersion() {
-    	$encryptor = new Encryptor();
-    	$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD);
-    	$this->assertNotEmpty($encrypted);
+    public function testCanEncryptWithDefaultVersion()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD);
+        $this->assertNotEmpty($encrypted);
     }
 
-    public function testCanEncryptWithVersion0() {
-    	$encryptor = new Encryptor();
-    	$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 0);
-    	$this->assertNotEmpty($encrypted);
+    public function testCanEncryptWithVersion0()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 0);
+        $this->assertNotEmpty($encrypted);
     }
     
-    public function testCanEncryptWithVersion1() {
-    	$encryptor = new Encryptor();
-    	$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 1);
-    	$this->assertNotEmpty($encrypted);
+    public function testCanEncryptWithVersion1()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 1);
+        $this->assertNotEmpty($encrypted);
     }
     
-    public function testCanEncryptWithVersion2() {
-    	$encryptor = new Encryptor();
-    	$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 2);
-    	$this->assertNotEmpty($encrypted);
+    public function testCanEncryptWithVersion2()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 2);
+        $this->assertNotEmpty($encrypted);
     }
 
-    public function testSelfEncryptedVersion0VectorIsVersion0() {
-    	$encryptor = new Encryptor();
-    	$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 0);
-    	$actualVersion = ord(substr(base64_decode($encrypted), 0, 1));
-    	$this->assertEquals(0, $actualVersion);
+    public function testSelfEncryptedVersion0VectorIsVersion0()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 0);
+        $actualVersion = ord(substr(base64_decode($encrypted), 0, 1));
+        $this->assertEquals(0, $actualVersion);
     }
     
-    public function testSelfEncryptedVersion1VectorIsVersion1() {
-    	$encryptor = new Encryptor();
-    	$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 1);
-    	$actualVersion = ord(substr(base64_decode($encrypted), 0, 1));
-    	$this->assertEquals(1, $actualVersion);
+    public function testSelfEncryptedVersion1VectorIsVersion1()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 1);
+        $actualVersion = ord(substr(base64_decode($encrypted), 0, 1));
+        $this->assertEquals(1, $actualVersion);
     }
 
-    public function testSelfEncryptedVersion2VectorIsVersion2() {
-    	$encryptor = new Encryptor();
-    	$encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 2);
-    	$actualVersion = ord(substr(base64_decode($encrypted), 0, 1));
-    	$this->assertEquals(2, $actualVersion);
+    public function testSelfEncryptedVersion2VectorIsVersion2()
+    {
+        $encryptor = new Encryptor;
+        $encrypted = $encryptor->encrypt(self::SAMPLE_PLAINTEXT, self::SAMPLE_PASSWORD, 2);
+        $actualVersion = ord(substr(base64_decode($encrypted), 0, 1));
+        $this->assertEquals(2, $actualVersion);
     }
-
 }

+ 160 - 158
tests/RNCryptor/VectorTest.php

@@ -1,250 +1,252 @@
 <?php
-namespace RNCryptor;
+namespace Tests\RNCryptor;
 
 use PHPUnit\Framework\TestCase;
-
-class VectorBase extends TestCase {
-
-	/**
-	 * Base directory for the test vector files,
-	 * relative to __DIR__
-	 */
-	const PARALLEL_VECTOR_DIR = '/../../../spec/vectors/CURRENT';
-	const SUBPACKAGE_VECTOR_DIR = '/../../vendor/rncryptor/spec/vectors/CURRENT';
-
-	public function testKdfVectorAllFieldsEmptyOrZero() {
-
-		$vector = $this->_getVectors('kdf')[0];
-
-        $cryptor = new Cryptor();
+use RNCryptor\RNCryptor\Cryptor;
+use RNCryptor\RNCryptor\Encryptor;
+
+class VectorBase extends TestCase
+{
+    /**
+     * Base directory for the test vector files,
+     * relative to __DIR__
+     */
+    const PARALLEL_VECTOR_DIR = '/../../../spec/vectors/CURRENT';
+    const SUBPACKAGE_VECTOR_DIR = '/../../vendor/rncryptor/spec/vectors/CURRENT';
+
+    public function testKdfVectorAllFieldsEmptyOrZero()
+    {
+        $vector = $this->getVectors('kdf')[0];
+
+        $cryptor = new Cryptor;
         $key = $cryptor->generateKey(
-            $this->_prettyHexToBin($vector['salt_hex']),
+            $this->prettyHexToBin($vector['salt_hex']),
             $vector['password'],
             $vector['version']
         );
 
-        $this->assertEquals($this->_prettyHexToBin($vector['key_hex']), $key);
-	}
-
-    public function testKdfVectorOneByte() {
+        $this->assertEquals($this->prettyHexToBin($vector['key_hex']), $key);
+    }
 
-        $vector = $this->_getVectors('kdf')[1];
+    public function testKdfVectorOneByte()
+    {
+        $vector = $this->getVectors('kdf')[1];
 
-        $cryptor = new Cryptor();
+        $cryptor = new Cryptor;
         $key = $cryptor->generateKey(
-            $this->_prettyHexToBin($vector['salt_hex']),
+            $this->prettyHexToBin($vector['salt_hex']),
             $vector['password'],
             $vector['version']
         );
 
-        $this->assertEquals($this->_prettyHexToBin($vector['key_hex']), $key);
+        $this->assertEquals($this->prettyHexToBin($vector['key_hex']), $key);
     }
 
-    public function testKdfVectorExactlyOneBlock() {
+    public function testKdfVectorExactlyOneBlock()
+    {
+        $vector = $this->getVectors('kdf')[2];
 
-        $vector = $this->_getVectors('kdf')[2];
-
-        $cryptor = new Cryptor();
+        $cryptor = new Cryptor;
         $key = $cryptor->generateKey(
-            $this->_prettyHexToBin($vector['salt_hex']),
+            $this->prettyHexToBin($vector['salt_hex']),
             $vector['password'],
             $vector['version']
         );
 
-        $this->assertEquals($this->_prettyHexToBin($vector['key_hex']), $key);
+        $this->assertEquals($this->prettyHexToBin($vector['key_hex']), $key);
     }
 
-    public function testKdfVectorMoreThanOneBlock() {
-
-        $vector = $this->_getVectors('kdf')[3];
+    public function testKdfVectorMoreThanOneBlock()
+    {
+        $vector = $this->getVectors('kdf')[3];
 
-        $cryptor = new Cryptor();
+        $cryptor = new Cryptor;
         $key = $cryptor->generateKey(
-            $this->_prettyHexToBin($vector['salt_hex']),
+            $this->prettyHexToBin($vector['salt_hex']),
             $vector['password'],
             $vector['version']
         );
 
-        $this->assertEquals($this->_prettyHexToBin($vector['key_hex']), $key);
+        $this->assertEquals($this->prettyHexToBin($vector['key_hex']), $key);
     }
 
-    public function testKeyVectorAllFieldsEmptyOrZero() {
-
-		$vector = $this->_getVectors('key')[0];
+    public function testKeyVectorAllFieldsEmptyOrZero()
+    {
+        $vector = $this->getVectors('key')[0];
 
-        $encryptor = new Encryptor();
+        $encryptor = new Encryptor;
         $encryptedB64 = $encryptor->encryptWithArbitraryKeys(
-            $this->_prettyHexToBin($vector['plaintext_hex']),
-            $this->_prettyHexToBin($vector['enc_key_hex']),
-            $this->_prettyHexToBin($vector['hmac_key_hex']),
-            $this->_prettyHexToBin($vector['iv_hex']),
+            $this->prettyHexToBin($vector['plaintext_hex']),
+            $this->prettyHexToBin($vector['enc_key_hex']),
+            $this->prettyHexToBin($vector['hmac_key_hex']),
+            $this->prettyHexToBin($vector['iv_hex']),
             $vector['version']
         );
 
-        $this->assertEquals($vector['ciphertext_hex'], $this->_binToPrettyHex(base64_decode($encryptedB64)));
-	}
-
-    public function testKeyVectorOneByte() {
+        $this->assertEquals($vector['ciphertext_hex'], $this->binToPrettyHex(base64_decode($encryptedB64)));
+    }
 
-        $vector = $this->_getVectors('key')[1];
+    public function testKeyVectorOneByte()
+    {
+        $vector = $this->getVectors('key')[1];
 
-        $encryptor = new Encryptor();
+        $encryptor = new Encryptor;
         $encryptedB64 = $encryptor->encryptWithArbitraryKeys(
-            $this->_prettyHexToBin($vector['plaintext_hex']),
-            $this->_prettyHexToBin($vector['enc_key_hex']),
-            $this->_prettyHexToBin($vector['hmac_key_hex']),
-            $this->_prettyHexToBin($vector['iv_hex']),
+            $this->prettyHexToBin($vector['plaintext_hex']),
+            $this->prettyHexToBin($vector['enc_key_hex']),
+            $this->prettyHexToBin($vector['hmac_key_hex']),
+            $this->prettyHexToBin($vector['iv_hex']),
             $vector['version']
         );
 
-        $this->assertEquals($vector['ciphertext_hex'], $this->_binToPrettyHex(base64_decode($encryptedB64)));
+        $this->assertEquals($vector['ciphertext_hex'], $this->binToPrettyHex(base64_decode($encryptedB64)));
     }
 
-    public function testKeyVectorExactlyOneBlock() {
-
-        $vector = $this->_getVectors('key')[2];
+    public function testKeyVectorExactlyOneBlock()
+    {
+        $vector = $this->getVectors('key')[2];
 
-        $encryptor = new Encryptor();
+        $encryptor = new Encryptor;
         $encryptedB64 = $encryptor->encryptWithArbitraryKeys(
-            $this->_prettyHexToBin($vector['plaintext_hex']),
-            $this->_prettyHexToBin($vector['enc_key_hex']),
-            $this->_prettyHexToBin($vector['hmac_key_hex']),
-            $this->_prettyHexToBin($vector['iv_hex']),
+            $this->prettyHexToBin($vector['plaintext_hex']),
+            $this->prettyHexToBin($vector['enc_key_hex']),
+            $this->prettyHexToBin($vector['hmac_key_hex']),
+            $this->prettyHexToBin($vector['iv_hex']),
             $vector['version']
         );
 
-        $this->assertEquals($vector['ciphertext_hex'], $this->_binToPrettyHex(base64_decode($encryptedB64)));
+        $this->assertEquals($vector['ciphertext_hex'], $this->binToPrettyHex(base64_decode($encryptedB64)));
     }
 
-    public function testKeyVectorMoreThanOneBlock() {
+    public function testKeyVectorMoreThanOneBlock()
+    {
+        $vector = $this->getVectors('key')[3];
 
-        $vector = $this->_getVectors('key')[3];
-
-        $encryptor = new Encryptor();
+        $encryptor = new Encryptor;
         $encryptedB64 = $encryptor->encryptWithArbitraryKeys(
-            $this->_prettyHexToBin($vector['plaintext_hex']),
-            $this->_prettyHexToBin($vector['enc_key_hex']),
-            $this->_prettyHexToBin($vector['hmac_key_hex']),
-            $this->_prettyHexToBin($vector['iv_hex']),
+            $this->prettyHexToBin($vector['plaintext_hex']),
+            $this->prettyHexToBin($vector['enc_key_hex']),
+            $this->prettyHexToBin($vector['hmac_key_hex']),
+            $this->prettyHexToBin($vector['iv_hex']),
             $vector['version']
         );
 
-        $this->assertEquals($vector['ciphertext_hex'], $this->_binToPrettyHex(base64_decode($encryptedB64)));
+        $this->assertEquals($vector['ciphertext_hex'], $this->binToPrettyHex(base64_decode($encryptedB64)));
     }
 
-    public function testPasswordVectorAllFieldsEmptyOrZero() {
-
-		$vector = $this->_getVectors('password')[0];
+    public function testPasswordVectorAllFieldsEmptyOrZero()
+    {
+        $vector = $this->getVectors('password')[0];
 
-        $encryptor = new Encryptor();
+        $encryptor = new Encryptor;
         $encryptedB64 = $encryptor->encryptWithArbitrarySalts(
-            $this->_prettyHexToBin($vector['plaintext_hex']),
+            $this->prettyHexToBin($vector['plaintext_hex']),
             $vector['password'],
-            $this->_prettyHexToBin($vector['enc_salt_hex']),
-            $this->_prettyHexToBin($vector['hmac_salt_hex']),
-            $this->_prettyHexToBin($vector['iv_hex']),
+            $this->prettyHexToBin($vector['enc_salt_hex']),
+            $this->prettyHexToBin($vector['hmac_salt_hex']),
+            $this->prettyHexToBin($vector['iv_hex']),
             $vector['version']
         );
 
-        $this->assertEquals($vector['ciphertext_hex'], $this->_binToPrettyHex(base64_decode($encryptedB64)));
-	}
-
-    public function testPasswordVectorOneByte() {
+        $this->assertEquals($vector['ciphertext_hex'], $this->binToPrettyHex(base64_decode($encryptedB64)));
+    }
 
-        $vector = $this->_getVectors('password')[1];
+    public function testPasswordVectorOneByte()
+    {
+        $vector = $this->getVectors('password')[1];
 
-        $encryptor = new Encryptor();
+        $encryptor = new Encryptor;
         $encryptedB64 = $encryptor->encryptWithArbitrarySalts(
-            $this->_prettyHexToBin($vector['plaintext_hex']),
+            $this->prettyHexToBin($vector['plaintext_hex']),
             $vector['password'],
-            $this->_prettyHexToBin($vector['enc_salt_hex']),
-            $this->_prettyHexToBin($vector['hmac_salt_hex']),
-            $this->_prettyHexToBin($vector['iv_hex']),
+            $this->prettyHexToBin($vector['enc_salt_hex']),
+            $this->prettyHexToBin($vector['hmac_salt_hex']),
+            $this->prettyHexToBin($vector['iv_hex']),
             $vector['version']
         );
 
-        $this->assertEquals($vector['ciphertext_hex'], $this->_binToPrettyHex(base64_decode($encryptedB64)));
+        $this->assertEquals($vector['ciphertext_hex'], $this->binToPrettyHex(base64_decode($encryptedB64)));
     }
 
-    public function testPasswordVectorExactlyOneBlock() {
-
-        $vector = $this->_getVectors('password')[2];
+    public function testPasswordVectorExactlyOneBlock()
+    {
+        $vector = $this->getVectors('password')[2];
 
-        $encryptor = new Encryptor();
+        $encryptor = new Encryptor;
         $encryptedB64 = $encryptor->encryptWithArbitrarySalts(
-            $this->_prettyHexToBin($vector['plaintext_hex']),
+            $this->prettyHexToBin($vector['plaintext_hex']),
             $vector['password'],
-            $this->_prettyHexToBin($vector['enc_salt_hex']),
-            $this->_prettyHexToBin($vector['hmac_salt_hex']),
-            $this->_prettyHexToBin($vector['iv_hex']),
+            $this->prettyHexToBin($vector['enc_salt_hex']),
+            $this->prettyHexToBin($vector['hmac_salt_hex']),
+            $this->prettyHexToBin($vector['iv_hex']),
             $vector['version']
         );
 
-        $this->assertEquals($vector['ciphertext_hex'], $this->_binToPrettyHex(base64_decode($encryptedB64)));
+        $this->assertEquals($vector['ciphertext_hex'], $this->binToPrettyHex(base64_decode($encryptedB64)));
     }
 
-    public function testPasswordVectorMoreThanOneBlock() {
-
-        $vector = $this->_getVectors('password')[3];
+    public function testPasswordVectorMoreThanOneBlock()
+    {
+        $vector = $this->getVectors('password')[3];
 
-        $encryptor = new Encryptor();
+        $encryptor = new Encryptor;
         $encryptedB64 = $encryptor->encryptWithArbitrarySalts(
-            $this->_prettyHexToBin($vector['plaintext_hex']),
+            $this->prettyHexToBin($vector['plaintext_hex']),
             $vector['password'],
-            $this->_prettyHexToBin($vector['enc_salt_hex']),
-            $this->_prettyHexToBin($vector['hmac_salt_hex']),
-            $this->_prettyHexToBin($vector['iv_hex']),
+            $this->prettyHexToBin($vector['enc_salt_hex']),
+            $this->prettyHexToBin($vector['hmac_salt_hex']),
+            $this->prettyHexToBin($vector['iv_hex']),
             $vector['version']
         );
 
-        $this->assertEquals($vector['ciphertext_hex'], $this->_binToPrettyHex(base64_decode($encryptedB64)));
+        $this->assertEquals($vector['ciphertext_hex'], $this->binToPrettyHex(base64_decode($encryptedB64)));
     }
 
-    private function _prettyHexToBin($data) {
-		return hex2bin(preg_replace("/[^a-z0-9]/i", '', $data));
-	}
-
-	private function _binToPrettyHex($data) {
-
-		$hex = bin2hex($data);
-
-		$prettyHex = '';
-		foreach (str_split($hex, 8) as $index => $part) {
-			$prettyHex .= ($index != 0 ? ' ' : '') . $part;
-		}
-		return $prettyHex;
-	}
-
-	private function _getVectors($filename) {
-
-		$absolutePath = __DIR__ . '/' . self::PARALLEL_VECTOR_DIR . '/' . $filename;
-		if (!file_exists($absolutePath)) {
-			$absolutePath = __DIR__ . '/' . self::SUBPACKAGE_VECTOR_DIR . '/' . $filename;
-			if (!file_exists($absolutePath)) {
-				throw new \Exception('No such file: ' . $absolutePath);
-			}
-		}
-
-		$index = -1;
-		$tests = array();
-		$fd = fopen($absolutePath, 'r');
-		while (!feof($fd)) {
-			$line = trim(fgets($fd));
-	
-			if (preg_match("/^\s*(\w+)\s*\:\s*(.*)/", $line, $match)) {
-				$key = strtolower($match[1]);
-				$value = trim($match[2]);
-	
-				if ($key == 'title') {
-					$index++;
-				}
-	
-				$tests[$index][$key] = $value;
-			}
-		}
-		fclose($fd);
-	
-		return $tests;
-	}
-	
+    private function prettyHexToBin($data)
+    {
+        return hex2bin(preg_replace("/[^a-z0-9]/i", '', $data));
+    }
+
+    private function binToPrettyHex($data)
+    {
+        $hex = bin2hex($data);
+
+        $prettyHex = '';
+        foreach (str_split($hex, 8) as $index => $part) {
+            $prettyHex .= ($index != 0 ? ' ' : '') . $part;
+        }
+        return $prettyHex;
+    }
+
+    private function getVectors($filename)
+    {
+        $absolutePath = __DIR__ . '/' . self::PARALLEL_VECTOR_DIR . '/' . $filename;
+        if (!file_exists($absolutePath)) {
+            $absolutePath = __DIR__ . '/' . self::SUBPACKAGE_VECTOR_DIR . '/' . $filename;
+            if (!file_exists($absolutePath)) {
+                throw new \Exception('No such file: ' . $absolutePath);
+            }
+        }
+
+        $index = -1;
+        $tests = array();
+        $fd = fopen($absolutePath, 'r');
+        while (!feof($fd)) {
+            $line = trim(fgets($fd));
+    
+            if (preg_match("/^\s*(\w+)\s*\:\s*(.*)/", $line, $match)) {
+                $key = strtolower($match[1]);
+                $value = trim($match[2]);
+    
+                if ($key == 'title') {
+                    $index++;
+                }
+    
+                $tests[$index][$key] = $value;
+            }
+        }
+        fclose($fd);
+    
+        return $tests;
+    }
 }