From: Stricted Date: Wed, 1 Jul 2015 04:36:25 +0000 (+0200) Subject: add methods to decrypt return data from router X-Git-Tag: 1.0.0~12 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=14d4f286d33b631a93207e4d13086c0a3bc0df12;p=GitHub%2FStricted%2Fspeedport-hybrid-php-api.git add methods to decrypt return data from router --- diff --git a/CryptLib/Cipher/Block/AbstractCipher.php b/CryptLib/Cipher/Block/AbstractCipher.php new file mode 100644 index 0000000..7df8ad5 --- /dev/null +++ b/CryptLib/Cipher/Block/AbstractCipher.php @@ -0,0 +1,217 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block; + +/** + * An abstract class for simplifing creation of ciphers + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + */ +abstract class AbstractCipher implements \CryptLib\Cipher\Block\Cipher { + + /** + * @var int The block size for the cipher + */ + protected $blockSize = 0; + + /** + * @var string The cipher name for the current instance + */ + protected $cipher = ''; + + /** + * @var boolean Is the cipher ready to encrypt/decrypt + */ + protected $initialized = false; + + /** + * @var string The key to use for encryption/decryption + */ + protected $key = ''; + + /** + * @var int The size of the key to use + */ + protected $keySize = 0; + + /** + * Decrypt a block of data + * + * @param string $data The ciphertext to decrypt + * + * @return string The decrypted data + */ + abstract protected function decryptBlockData($data); + + /** + * Encrypt a block of data + * + * @param string $data The plaintext to encrypt + * + * @return string The encrypted cipher text + */ + abstract protected function encryptBlockData($data); + + /** + * Construct the instance for the supplied cipher name + * + * @param string $cipher The cipher to implement + * + * @return void + * @throws InvalidArgumentException if the cipher is not supported + */ + public function __construct($cipher) { + $ciphers = static::getSupportedCiphers(); + if (in_array($cipher, $ciphers)) { + $this->cipher = $cipher; + } else { + throw new \InvalidArgumentException('Unsupported Cipher Supplied'); + } + } + + /** + * Decrypt a block of data using the supplied string key. + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to decrypt + * + * @return string The result decrypted data + * @throws InvalidArgumentException If the data size is not the block size + * @throws RuntimeException If the cipher is not initialized + */ + public function decryptBlock($data) { + $this->enforceInitializedCipher(); + $this->enforceProperBlockSize($data); + return $this->decryptBlockData($data); + } + + /** + * Encrypt a block of data using the supplied string key. + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to encrypt + * + * @return string The result encrypted data + * @throws InvalidArgumentException If the data size is not the block size + * @throws RuntimeException If the cipher is not initialized + */ + public function encryptBlock($data) { + $this->enforceInitializedCipher(); + $this->enforceProperBlockSize($data); + return $this->encryptBlockData($data); + } + + /** + * Get the block size for the current initialized cipher + * + * @param string $key The key the data will be encrypted with + * + * @return int The block size for the current cipher + */ + public function getBlockSize() { + return $this->blockSize; + } + + /** + * Get the string name of the current cipher instance + * + * @return string The current instantiated cipher + */ + public function getCipher() { + return $this->cipher; + } + + /** + * Get the key size for the current initialized cipher + * + * @return int The key size for the current cipher + */ + public function getKeySize() { + return $this->keySize; + } + + /** + * Set the key to use for the cipher + * + * @param string $key The key to use + * + * @throws InvalidArgumentException If the key is not the correct size + * @return void + */ + public function setKey($key) { + if (strlen($key) != $this->getKeySize()) { + throw new \InvalidArgumentException( + sprintf( + 'The supplied key block is not the correct size [%d:%d]', + strlen($key), + $this->getKeySize() + ) + ); + } + $this->key = $key; + $this->initialized = $this->initialize(); + } + + /** + * Check to see if the cipher is initialized + * + * @return void + * @throws RuntimeException If the cipher is not initialized + */ + protected function enforceInitializedCipher() { + if (!$this->initialized) { + throw new \RuntimeException( + 'The cipher has not been properly initialized' + ); + } + } + + /** + * Check to see if the data is of the correct block size + * + * @param string $data The data block to check + * + * @return void + * @throws InvalidArgumentException if the data is not the correct size + */ + protected function enforceProperBlockSize($data) { + if (strlen($data) != $this->getBlockSize()) { + throw new \InvalidArgumentException( + sprintf( + 'The supplied data block is not the correct size [%d:%d]', + strlen($data), + $this->getBlockSize() + ) + ); + } + } + + /** + * Initialize the function after the key is set + * + * @return boolean The status of the initialization + */ + protected function initialize() { + return true; + } + +} \ No newline at end of file diff --git a/CryptLib/Cipher/Block/AbstractMode.php b/CryptLib/Cipher/Block/AbstractMode.php new file mode 100644 index 0000000..5bdef45 --- /dev/null +++ b/CryptLib/Cipher/Block/AbstractMode.php @@ -0,0 +1,166 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block; + +/** + * An abstract class for simplifing creation of cipher modes + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + */ +abstract class AbstractMode implements \CryptLib\Cipher\Block\Mode { + + /** + * @var string Additional data to authenticate with + */ + protected $adata = ''; + + /** + * @var Cipher The cipher to use for this mode instance + */ + protected $cipher = null; + + /** + * @var string The initialization Vector to use for this mode + */ + protected $initv = ''; + + /** + * @var string The mode name for the current instance + */ + protected $mode = ''; + + /** + * @var array Mode specific options + */ + protected $options = array(); + + /** + * @var string The internal state of the mode + */ + protected $state = ''; + + /** + * Perform the decryption of the current block + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + abstract protected function decryptBlock($data); + + /** + * Perform the encryption of the current block + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + abstract protected function encryptBlock($data); + + /** + * Build the instance of the cipher mode + * + * @param Cipher $cipher The cipher to use for encryption/decryption + * @param string $initv The initialization vector (empty if not needed) + * @param array $options An array of mode-specific options + */ + public function __construct( + \CryptLib\Cipher\Block\Cipher $cipher, + $initv, + array $options = array() + ) { + $class = strtolower(get_class($this)); + $class = substr($class, strrpos($class, '\\') + 1); + $this->mode = $class; + $this->options = $options + $this->options; + $this->cipher = $cipher; + $this->initv = $initv; + $this->reset(); + } + + /** + * Decrypt the data using the supplied key, cipher and initialization vector + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + public function decrypt($data) { + $this->enforceBlockSize($data); + return $this->decryptBlock($data); + } + + /** + * Encrypt the data using the supplied key, cipher and initialization vector + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + public function encrypt($data) { + $this->enforceBlockSize($data); + return $this->encryptBlock($data); + } + + /** + * Finish the mode and append any additional data necessary + * + * @return string Any additional data + */ + public function finish() { + return ''; + } + + /** + * Get the name of the current mode implementation + * + * @return string The current mode name + */ + public function getMode() { + return $this->mode; + } + + /** + * Reset the mode to start over (destroying any intermediate state) + * + * @return void + */ + public function reset() { + $this->state = $this->initv; + } + + /** + * Enforce the data block is the correct size for the cipher + * + * @param string $data The data to check + * + * @return void + * @throws InvalidArgumentException if the block size is not correct + */ + protected function enforceBlockSize($data) { + if (strlen($data) != $this->cipher->getBlockSize()) { + throw new \InvalidArgumentException( + sprintf( + 'The data block must match the block size [%d:%d]', + strlen($data), + $this->cipher->getBlockSize() + ) + ); + } + } +} \ No newline at end of file diff --git a/CryptLib/Cipher/Block/Cipher.php b/CryptLib/Cipher/Block/Cipher.php new file mode 100644 index 0000000..e250ab5 --- /dev/null +++ b/CryptLib/Cipher/Block/Cipher.php @@ -0,0 +1,90 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block; + +/** + * The interface that all block ciphers must implement + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +interface Cipher { + + /** + * Get a list of supported ciphers by this cipher. + * + * @return array An array of supported cipher names (strings) + */ + public static function getSupportedCiphers(); + + /** + * Decrypt a block of data using the supplied string key. + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to decrypt + * + * @return string The result decrypted data + */ + public function decryptBlock($data); + + /** + * Encrypt a block of data using the supplied string key. + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to encrypt + * + * @return string The result encrypted data + */ + public function encryptBlock($data); + + /** + * Get the block size for the current initialized cipher. + * + * @return int The block size for the current cipher + */ + public function getBlockSize(); + + /** + * Get the key size for the current initialized cipher + * + * @return int The key size for the current cipher + */ + public function getKeySize(); + + /** + * Get the string name of the current cipher instance. + * + * @return string The current instantiated cipher + */ + public function getCipher(); + + /** + * Set the key to be used in this instance + * + * @param string $key The key the data will be encrypted with + * + * @return void + */ + public function setKey($key); + +} diff --git a/CryptLib/Cipher/Block/Cipher/AES.php b/CryptLib/Cipher/Block/Cipher/AES.php new file mode 100644 index 0000000..41d9785 --- /dev/null +++ b/CryptLib/Cipher/Block/Cipher/AES.php @@ -0,0 +1,59 @@ + + * @author Jim Wigginton + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Cipher; + +/** + * An implementation of the AES cipher, using the phpseclib implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ +class AES extends Rijndael { + + /** + * Get a list of supported ciphers for this class implementation + * + * @return array A list of supported ciphers + */ + public static function getSupportedCiphers() { + return array( + 'aes-128', + 'aes-192', + 'aes-256', + ); + } + + /** + * Construct the instance for the supplied cipher name + * + * @param string $cipher The cipher to implement + * + * @return void + * @throws InvalidArgumentException if the cipher is not supported + */ + public function __construct($cipher) { + parent::__construct($cipher); + list (, $bits) = explode('-', $cipher, 2); + $this->setBlockSize(128); + $this->setKeySize($bits); + } + +} diff --git a/CryptLib/Cipher/Block/Cipher/DES.php b/CryptLib/Cipher/Block/Cipher/DES.php new file mode 100644 index 0000000..9d8c027 --- /dev/null +++ b/CryptLib/Cipher/Block/Cipher/DES.php @@ -0,0 +1,396 @@ + + * @author Jim Wigginton + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Cipher; + +/** + * An implementation of the DES cipher, using the phpseclib implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ +class DES extends \CryptLib\Cipher\Block\AbstractCipher { + + /** + * @var int The block size for the cipher + */ + protected $blockSize = 8; + + /** + * @var int The key size for the cipher + */ + protected $keySize = 8; + + /** + * @var array The prepared key schedule + */ + protected $preparedKeys = array(); + + /** + * In the official DES docs, they're described as being matrices that one + * accesses by using the first and last bits to determine the row and the + * middle four bits to determine the column. in this implementation, + * they've been converted to vectors + * + * @var array The S-boxes + */ + protected static $sbox = array( + array( + 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, + 3, 10 ,10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, + 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, + 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13 + ), + array( + 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, + 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, + 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, + 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9 + ), + array( + 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, + 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, + 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, + 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12 + ), + array( + 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, + 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, + 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, + 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14 + ), + array( + 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, + 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, + 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, + 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3 + ), + array( + 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, + 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, + 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, + 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13 + ), + array( + 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, + 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, + 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, + 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12 + ), + array( + 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, + 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, + 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, + 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 + ) + ); + + /** + * @var array The key shift sequence for shifts per round of DES block + */ + protected static $keyShifts = array( + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 + ); + + /** + * Get a list of supported ciphers for this class implementation + * + * @return array A list of supported ciphers + */ + public static function getSupportedCiphers() { + return array('des'); + } + + /** + * Decrypt a block of data using the supplied string key + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to decrypt + * + * @return string The result decrypted data + */ + protected function decryptBlockData($data) { + $keys = array_reverse($this->preparedKeys); + return $this->processBlock($data, $keys); + } + + /** + * Encrypt a block of data using the supplied string key + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to encrypt + * + * @return string The result encrypted data + */ + protected function encryptBlockData($data) { + return $this->processBlock($data, $this->preparedKeys); + } + + /** + * Initialize the cipher by preparing the key + * + * @return boolean The status of the initialization + */ + protected function initialize() { + $this->preparedKeys = $this->prepareKey($this->key); + return true; + } + + /** + * Compute the keys necessary to execute the block cipher + * + * @param string $key The key to prepare + * + * @return array The prepared keys + */ + private function prepareKey($key) { + // pad the key and remove extra characters as appropriate. + $key = str_pad(substr($key, 0, 8), 8, chr(0)); + + $temp = unpack('Na/Nb', $key); + $key = array($temp['a'], $temp['b']); + $msb = array( + ($key[0] >> 31) & 1, + ($key[1] >> 31) & 1 + ); + $key[0] &= 0x7FFFFFFF; + $key[1] &= 0x7FFFFFFF; + + $key = array( + (($key[1] & 0x00000002) << 26) | (($key[1] & 0x00000204) << 17) | + (($key[1] & 0x00020408) << 8) | (($key[1] & 0x02040800) >> 1) | + (($key[0] & 0x00000002) << 22) | (($key[0] & 0x00000204) << 13) | + (($key[0] & 0x00020408) << 4) | (($key[0] & 0x02040800) >> 5) | + (($key[1] & 0x04080000) >> 10) | (($key[0] & 0x04080000) >> 14) | + (($key[1] & 0x08000000) >> 19) | (($key[0] & 0x08000000) >> 23) | + (($key[0] & 0x00000010) >> 1) | (($key[0] & 0x00001000) >> 10) | + (($key[0] & 0x00100000) >> 19) | (($key[0] & 0x10000000) >> 28), + (($key[1] & 0x00000080) << 20) | (($key[1] & 0x00008000) << 11) | + (($key[1] & 0x00800000) << 2) | (($key[0] & 0x00000080) << 16) | + (($key[0] & 0x00008000) << 7) | (($key[0] & 0x00800000) >> 2) | + (($key[1] & 0x00000040) << 13) | (($key[1] & 0x00004000) << 4) | + (($key[1] & 0x00400000) >> 5) | (($key[1] & 0x40000000) >> 14) | + (($key[0] & 0x00000040) << 9) | ( $key[0] & 0x00004000 ) | + (($key[0] & 0x00400000) >> 9) | (($key[0] & 0x40000000) >> 18) | + (($key[1] & 0x00000020) << 6) | (($key[1] & 0x00002000) >> 3) | + (($key[1] & 0x00200000) >> 12) | (($key[1] & 0x20000000) >> 21) | + (($key[0] & 0x00000020) << 2) | (($key[0] & 0x00002000) >> 7) | + (($key[0] & 0x00200000) >> 16) | (($key[0] & 0x20000000) >> 25) | + (($key[1] & 0x00000010) >> 1) | (($key[1] & 0x00001000) >> 10) | + (($key[1] & 0x00100000) >> 19) | (($key[1] & 0x10000000) >> 28) | + ($msb[1] << 24) | ($msb[0] << 20) + ); + + $keys = array(); + for ($i = 0; $i < 16; $i++) { + $key[0] <<= static::$keyShifts[$i]; + $temp = ($key[0] & 0xF0000000) >> 28; + $key[0] = ($key[0] | $temp) & 0x0FFFFFFF; + + $key[1] <<= static::$keyShifts[$i]; + $temp = ($key[1] & 0xF0000000) >> 28; + $key[1] = ($key[1] | $temp) & 0x0FFFFFFF; + + $temp = array( + (($key[1] & 0x00004000) >> 9) | (($key[1] & 0x00000800) >> 7) | + (($key[1] & 0x00020000) >> 14) | (($key[1] & 0x00000010) >> 2) | + (($key[1] & 0x08000000) >> 26) | (($key[1] & 0x00800000) >> 23), + (($key[1] & 0x02400000) >> 20) | (($key[1] & 0x00000001) << 4) | + (($key[1] & 0x00002000) >> 10) | (($key[1] & 0x00040000) >> 18) | + (($key[1] & 0x00000080) >> 6), + ( $key[1] & 0x00000020 ) | (($key[1] & 0x00000200) >> 5) | + (($key[1] & 0x00010000) >> 13) | (($key[1] & 0x01000000) >> 22) | + (($key[1] & 0x00000004) >> 1) | (($key[1] & 0x00100000) >> 20), + (($key[1] & 0x00001000) >> 7) | (($key[1] & 0x00200000) >> 17) | + (($key[1] & 0x00000002) << 2) | (($key[1] & 0x00000100) >> 6) | + (($key[1] & 0x00008000) >> 14) | (($key[1] & 0x04000000) >> 26), + (($key[0] & 0x00008000) >> 10) | ( $key[0] & 0x00000010 ) | + (($key[0] & 0x02000000) >> 22) | (($key[0] & 0x00080000) >> 17) | + (($key[0] & 0x00000200) >> 8) | (($key[0] & 0x00000002) >> 1), + (($key[0] & 0x04000000) >> 21) | (($key[0] & 0x00010000) >> 12) | + (($key[0] & 0x00000020) >> 2) | (($key[0] & 0x00000800) >> 9) | + (($key[0] & 0x00800000) >> 22) | (($key[0] & 0x00000100) >> 8), + (($key[0] & 0x00001000) >> 7) | (($key[0] & 0x00000088) >> 3) | + (($key[0] & 0x00020000) >> 14) | (($key[0] & 0x00000001) << 2) | + (($key[0] & 0x00400000) >> 21), + (($key[0] & 0x00000400) >> 5) | (($key[0] & 0x00004000) >> 10) | + (($key[0] & 0x00000040) >> 3) | (($key[0] & 0x00100000) >> 18) | + (($key[0] & 0x08000000) >> 26) | (($key[0] & 0x01000000) >> 24) + ); + + $keys[] = $temp; + } + + return $keys; + } + + /** + * Process a block of data and encrypt (or decrypt depending upon key sequence) + * + * @param string $block The block of data to process + * @param array $keys The array of prepared keys to use + * + * @return string The processed block data + */ + protected function processBlock($block, array $keys) { + $temp = unpack('Na/Nb', $block); + $block = array($temp['a'], $temp['b']); + + /** + * Because php does arithmetic right shifts, if the most significant bits + * are set, right shifting those into the correct position will add 1's - + * not 0's. this will intefere with the | operation unless a second & is + * done. so we isolate these bits and left shift them into place. we + * then & each block with 0x7FFFFFFF to prevennt 1's from being added for + * any other shifts. + */ + $msb = array( + ($block[0] >> 31) & 1, + ($block[1] >> 31) & 1 + ); + $block[0] &= 0x7FFFFFFF; + $block[1] &= 0x7FFFFFFF; + + /** + * We isolate the appropriate bit in the appropriate integer and shift as + * appropriate. In some cases, there are going to be multiple bits in the + * same integer that need to be shifted in the same way. we combine those + * into one shift operation. + */ + $block = array( + (($block[1] & 0x00000040) << 25) | (($block[1] & 0x00004000) << 16) | + (($block[1] & 0x00400001) << 7) | (($block[1] & 0x40000100) >> 2) | + (($block[0] & 0x00000040) << 21) | (($block[0] & 0x00004000) << 12) | + (($block[0] & 0x00400001) << 3) | (($block[0] & 0x40000100) >> 6) | + (($block[1] & 0x00000010) << 19) | (($block[1] & 0x00001000) << 10) | + (($block[1] & 0x00100000) << 1) | (($block[1] & 0x10000000) >> 8) | + (($block[0] & 0x00000010) << 15) | (($block[0] & 0x00001000) << 6) | + (($block[0] & 0x00100000) >> 3) | (($block[0] & 0x10000000) >> 12) | + (($block[1] & 0x00000004) << 13) | (($block[1] & 0x00000400) << 4) | + (($block[1] & 0x00040000) >> 5) | (($block[1] & 0x04000000) >> 14) | + (($block[0] & 0x00000004) << 9) | ( $block[0] & 0x00000400 ) | + (($block[0] & 0x00040000) >> 9) | (($block[0] & 0x04000000) >> 18) | + (($block[1] & 0x00010000) >> 11) | (($block[1] & 0x01000000) >> 20) | + (($block[0] & 0x00010000) >> 15) | (($block[0] & 0x01000000) >> 24), + (($block[1] & 0x00000080) << 24) | (($block[1] & 0x00008000) << 15) | + (($block[1] & 0x00800002) << 6) | (($block[0] & 0x00000080) << 20) | + (($block[0] & 0x00008000) << 11) | (($block[0] & 0x00800002) << 2) | + (($block[1] & 0x00000020) << 18) | (($block[1] & 0x00002000) << 9) | + ( $block[1] & 0x00200000 ) | (($block[1] & 0x20000000) >> 9) | + (($block[0] & 0x00000020) << 14) | (($block[0] & 0x00002000) << 5) | + (($block[0] & 0x00200000) >> 4) | (($block[0] & 0x20000000) >> 13) | + (($block[1] & 0x00000008) << 12) | (($block[1] & 0x00000800) << 3) | + (($block[1] & 0x00080000) >> 6) | (($block[1] & 0x08000000) >> 15) | + (($block[0] & 0x00000008) << 8) | (($block[0] & 0x00000800) >> 1) | + (($block[0] & 0x00080000) >> 10) | (($block[0] & 0x08000000) >> 19) | + (($block[1] & 0x00000200) >> 3) | (($block[0] & 0x00000200) >> 7) | + (($block[1] & 0x00020000) >> 12) | (($block[1] & 0x02000000) >> 21) | + (($block[0] & 0x00020000) >> 16) | (($block[0] & 0x02000000) >> 25) | + ($msb[1] << 28) | ($msb[0] << 24) + ); + + for ($i = 0; $i < 16; $i++) { + // start of "the Feistel (F) function" + $key = ((($block[1] >> 27) & 0x1F) + | (($block[1] & 1) << 5)) ^ $keys[$i][0]; + $temp = ((static::$sbox[0][$key]) << 28); + $key = (($block[1] & 0x1F800000) >> 23) ^ $keys[$i][1]; + $temp |= ((static::$sbox[1][$key]) << 24); + $key = (($block[1] & 0x01F80000) >> 19) ^ $keys[$i][2]; + $temp |= ((static::$sbox[2][$key]) << 20); + $key = (($block[1] & 0x001F8000) >> 15) ^ $keys[$i][3]; + $temp |= ((static::$sbox[3][$key]) << 16); + $key = (($block[1] & 0x0001F800) >> 11) ^ $keys[$i][4]; + $temp |= ((static::$sbox[4][$key]) << 12); + $key = (($block[1] & 0x00001F80) >> 7) ^ $keys[$i][5]; + $temp |= ((static::$sbox[5][$key]) << 8); + $key = (($block[1] & 0x000001F8) >> 3) ^ $keys[$i][6]; + $temp |= ((static::$sbox[6][$key]) << 4); + $key = ((($block[1] & 0x1F) << 1) + | (($block[1] >> 31) & 1)) ^ $keys[$i][7]; + $temp |= ( static::$sbox[7][$key]); + + $msb = ($temp >> 31) & 1; + $temp &= 0x7FFFFFFF; + $nwB = (($temp & 0x00010000) << 15) | (($temp & 0x02020120) << 5); + $nwB |= (($temp & 0x00001800) << 17) | (($temp & 0x01000000) >> 10); + $nwB |= (($temp & 0x00000008) << 24) | (($temp & 0x00100000) << 6); + $nwB |= (($temp & 0x00000010) << 21) | (($temp & 0x00008000) << 9); + $nwB |= (($temp & 0x00000200) << 12) | (($temp & 0x10000000) >> 27); + $nwB |= (($temp & 0x00000040) << 14) | (($temp & 0x08000000) >> 8); + $nwB |= (($temp & 0x00004000) << 4) | (($temp & 0x00000002) << 16); + $nwB |= (($temp & 0x00442000) >> 6) | (($temp & 0x40800000) >> 15); + $nwB |= (($temp & 0x00000001) << 11) | (($temp & 0x20000000) >> 20); + $nwB |= (($temp & 0x00080000) >> 13) | (($temp & 0x00000004) << 3); + $nwB |= (($temp & 0x04000000) >> 22) | (($temp & 0x00000480) >> 7); + $nwB |= (($temp & 0x00200000) >> 19) | ($msb << 23); + // end of "the Feistel (F) function" - $newBlock is F's output + + $temp = $block[1]; + $block[1] = $block[0] ^ $nwB; + $block[0] = $temp; + } + + $msb = array( + ($block[0] >> 31) & 1, + ($block[1] >> 31) & 1 + ); + $block[0] &= 0x7FFFFFFF; + $block[1] &= 0x7FFFFFFF; + + $block = array( + (($block[0] & 0x01000004) << 7) | (($block[1] & 0x01000004) << 6) | + (($block[0] & 0x00010000) << 13) | (($block[1] & 0x00010000) << 12) | + (($block[0] & 0x00000100) << 19) | (($block[1] & 0x00000100) << 18) | + (($block[0] & 0x00000001) << 25) | (($block[1] & 0x00000001) << 24) | + (($block[0] & 0x02000008) >> 2) | (($block[1] & 0x02000008) >> 3) | + (($block[0] & 0x00020000) << 4) | (($block[1] & 0x00020000) << 3) | + (($block[0] & 0x00000200) << 10) | (($block[1] & 0x00000200) << 9) | + (($block[0] & 0x00000002) << 16) | (($block[1] & 0x00000002) << 15) | + (($block[0] & 0x04000000) >> 11) | (($block[1] & 0x04000000) >> 12) | + (($block[0] & 0x00040000) >> 5) | (($block[1] & 0x00040000) >> 6) | + (($block[0] & 0x00000400) << 1) | ( $block[1] & 0x00000400 ) | + (($block[0] & 0x08000000) >> 20) | (($block[1] & 0x08000000) >> 21) | + (($block[0] & 0x00080000) >> 14) | (($block[1] & 0x00080000) >> 15) | + (($block[0] & 0x00000800) >> 8) | (($block[1] & 0x00000800) >> 9), + (($block[0] & 0x10000040) << 3) | (($block[1] & 0x10000040) << 2) | + (($block[0] & 0x00100000) << 9) | (($block[1] & 0x00100000) << 8) | + (($block[0] & 0x00001000) << 15) | (($block[1] & 0x00001000) << 14) | + (($block[0] & 0x00000010) << 21) | (($block[1] & 0x00000010) << 20) | + (($block[0] & 0x20000080) >> 6) | (($block[1] & 0x20000080) >> 7) | + ( $block[0] & 0x00200000 ) | (($block[1] & 0x00200000) >> 1) | + (($block[0] & 0x00002000) << 6) | (($block[1] & 0x00002000) << 5) | + (($block[0] & 0x00000020) << 12) | (($block[1] & 0x00000020) << 11) | + (($block[0] & 0x40000000) >> 15) | (($block[1] & 0x40000000) >> 16) | + (($block[0] & 0x00400000) >> 9) | (($block[1] & 0x00400000) >> 10) | + (($block[0] & 0x00004000) >> 3) | (($block[1] & 0x00004000) >> 4) | + (($block[0] & 0x00800000) >> 18) | (($block[1] & 0x00800000) >> 19) | + (($block[0] & 0x00008000) >> 12) | (($block[1] & 0x00008000) >> 13) | + ($msb[0] << 7) | ($msb[1] << 6) + ); + + return pack('NN', $block[0], $block[1]); + } + +} diff --git a/CryptLib/Cipher/Block/Cipher/MCrypt.php b/CryptLib/Cipher/Block/Cipher/MCrypt.php new file mode 100644 index 0000000..527a28e --- /dev/null +++ b/CryptLib/Cipher/Block/Cipher/MCrypt.php @@ -0,0 +1,152 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Cipher; + +/** + * The mcrypt block cipher implementation + * + * This class is used above all other implementations since it uses a core PHP + * library if it is available. + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ +class MCrypt extends \CryptLib\Cipher\Block\AbstractCipher { + + /** + * @var resource The mcrypt resource for cipher operations + */ + protected $mcrypt = null; + + /** + * Get a list of supported ciphers for this class implementation + * + * @return array A list of supported ciphers + */ + public static function getSupportedCiphers() { + // @codeCoverageIgnoreStart + if (!function_exists('mcrypt_list_algorithms')) { + return array(); + } + // @codeCoverageIgnoreEnd + return mcrypt_list_algorithms(); + } + + /** + * Construct the instance for the supplied cipher name + * + * @param string $cipher The cipher to implement + * + * @return void + * @throws InvalidArgumentException if the cipher is not supported + */ + public function __construct($cipher) { + parent::__construct($cipher); + $this->keySize = mcrypt_get_key_size($cipher, MCRYPT_MODE_ECB); + $this->blockSize = mcrypt_get_block_size($cipher, MCRYPT_MODE_ECB); + } + + /** + * Destroy the mcrypt module if it's open + * + * @return void + */ + public function __destruct() { + if ($this->mcrypt) { + mcrypt_module_close($this->mcrypt); + } + } + + /** + * Set the key to use for the cipher + * + * @param string $key The key to use + * + * @throws InvalidArgumentException If the key is not the correct size + * @return void + */ + public function setKey($key) { + switch ($this->cipher) { + case 'rijndael-128': + case 'rijndael-192': + case 'rijndael-256': + if (!in_array(strlen($key), array(16, 20, 24, 28, 32))) { + throw new \InvalidArgumentException( + sprintf( + 'The supplied key block is in the valid sizes [%d:%s]', + strlen($key), + '16, 20, 24, 28, 32' + ) + ); + } + $this->keySize = strlen($key); + default: + parent::setKey($key); + } + } + + /** + * Decrypt a block of data using the supplied string key + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to decrypt + * + * @return string The result decrypted data + */ + protected function decryptBlockData($data) { + return mdecrypt_generic($this->mcrypt, $data); + } + + /** + * Encrypt a block of data using the supplied string key + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to encrypt + * + * @return string The result encrypted data + */ + protected function encryptBlockData($data) { + return mcrypt_generic($this->mcrypt, $data); + } + + /** + * Initialize the cipher by preparing the key + * + * @return boolean The status of the initialization + * @codeCoverageIgnore + */ + protected function initialize() { + if ($this->mcrypt) { + mcrypt_module_close($this->mcrypt); + } + $this->mcrypt = mcrypt_module_open($this->cipher, '', MCRYPT_MODE_ECB, ''); + if ($this->mcrypt) { + $initv = str_repeat(chr(0), $this->getBlockSize()); + return false !== mcrypt_generic_init($this->mcrypt, $this->key, $initv); + } + return false; + } + +} diff --git a/CryptLib/Cipher/Block/Cipher/Rijndael.php b/CryptLib/Cipher/Block/Cipher/Rijndael.php new file mode 100644 index 0000000..c451a51 --- /dev/null +++ b/CryptLib/Cipher/Block/Cipher/Rijndael.php @@ -0,0 +1,655 @@ + + * @author Jim Wigginton + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Cipher; + +/** + * An implementation of the Rijndael cipher, using the phpseclib implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ +class Rijndael extends \CryptLib\Cipher\Block\AbstractCipher { + + /** + * @var int The number of bytes in each block + */ + protected $blockSize = 16; + + /** + * @var array The decryption key schedule + */ + protected $decryptionSchedule = array(); + + /** + * @var array The encryption key schedule + */ + protected $encryptionSchedule = array(); + + /** + * @var int The number of bytes in the key + */ + protected $keySize = 16; + + /** + * @var array The shift offsets used by the cipher + */ + protected $shiftOffsets = array(0, 1, 2, 3); + + /** + * @var array The rcon static array + */ + protected static $rcon = array(0, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, + 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000, + 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000, + 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000, + 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000 + ); + + /** + * @var array The initial t array used by the cipher + */ + protected static $tValues = array( + 0 => array(), + 1 => array(), + 2 => array(), + 3 => array( + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, + 0x6F6FB1DE, 0xC5C55491, 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, + 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, 0xCACA458F, 0x82829D1F, + 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, + 0x727296E4, 0xC0C05B9B, 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, + 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, 0x34345C68, 0xA5A5F451, + 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, + 0x05050F0A, 0x9A9AB52F, 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, + 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, 0x09091B12, 0x83839E1D, + 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, + 0x2F2F715E, 0x84849713, 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, + 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, 0x6A6ABED4, 0xCBCB468D, + 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, + 0x33335566, 0x85859411, 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, + 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, 0x5151F3A2, 0xA3A3FE5D, + 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, + 0xF3F30EFD, 0xD2D26DBF, 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, + 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, 0xC4C45793, 0xA7A7F255, + 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, + 0x9090AB3B, 0x8888830B, 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, + 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, 0xE0E03BDB, 0x32325664, + 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, + 0xE4E437D3, 0x79798BF2, 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, + 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, 0x6C6CB4D8, 0x5656FAAC, + 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, + 0xB4B4C773, 0xC6C65197, 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, + 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, 0x707090E0, 0x3E3E427C, + 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, + 0x1D1D273A, 0x9E9EB927, 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, + 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, 0x9B9BB62D, 0x1E1E223C, + 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, + 0x4242C684, 0x6868B8D0, 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, + 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C + ) + ); + + /** + * @var array The initial Decryption t value array + */ + protected static $dtValues = array( + 0 => array(), + 1 => array(), + 2 => array(), + 3 => array( + 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, + 0xFA58ABAC, 0xE303934B, 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, + 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, 0xB15A49DE, 0xBA1B6725, + 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, + 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, + 0xE0692949, 0xC9C8448E, 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, + 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, 0xDF4A1863, 0x1A3182E5, + 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, + 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, + 0x1F8F57E3, 0x55AB2A66, 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, + 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, 0xCF1C2B8A, 0x79B492A7, + 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, + 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, + 0x719F065E, 0x6E1051BD, 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, + 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, 0x98FB2419, 0xBDE997D6, + 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, + 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, + 0x1170AC1E, 0x5A724E6C, 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, + 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, 0x0A67B10C, 0x57E70F93, + 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, + 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, + 0xB6A8B92D, 0x1EA9C814, 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, + 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, 0x4329768B, 0x23C6DCCB, + 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, + 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, + 0x8652EC0D, 0xC1E3D077, 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, + 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, 0x494EC787, 0x38D1C1D9, + 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, + 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, + 0x39F75E2E, 0xC3AFF582, 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, + 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, 0x267809CD, 0x5918F46E, + 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, + 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, + 0xA59430C6, 0xA266C035, 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, + 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, 0x4DD68D76, 0xEFB04D43, + 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, + 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, + 0x105633E9, 0xD647136D, 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, + 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, 0xD2DF599C, 0xF2733F55, + 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, + 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, + 0x3C498B28, 0x0D9541FF, 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, + 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 + ) + ); + + /** + * @var array The sboxes used for key expansion + */ + protected static $sboxes = array( + 0 => array( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, + 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, + 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, + 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, + 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, + 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, + 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, + 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, + 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, + 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, + 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, + 0xB0, 0x54, 0xBB, 0x16 + ), + 1 => array(), + 2 => array(), + 3 => array(), + ); + + /** + * @var array The inverse sboxes used for decryption + */ + protected static $invSBoxes = array( + 0 => array( + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, + 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, + 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, + 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, + 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, + 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, + 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, + 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, + 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, + 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0C, 0x7D + ), + 1 => array(), + 2 => array(), + 3 => array(), + ); + + /** + * Get a list of supported ciphers for this class implementation + * + * @return array A list of supported ciphers + */ + public static function getSupportedCiphers() { + return array( + 'rijndael-128', + 'rijndael-160', + 'rijndael-192', + 'rijndael-224', + 'rijndael-256', + ); + } + + /** + * Initialize the tValues,dtValues,sboxes and inverseSBoxes arrays + * by calculating. Since this only needs to be done once, it's + * a static method + * + * @return void + */ + protected static function init() { + if (empty(static::$tValues[0])) { + $t_3 = static::$tValues[3]; + $dt3 = static::$dtValues[3]; + for ($i = 0; $i < 256; $i++) { + static::$tValues[2][$i << 8] = (($t_3[$i] << 8) & 0xFFFFFF00) + | (($t_3[$i] >> 24) & 0x000000FF); + static::$tValues[1][$i << 16] = (($t_3[$i] << 16) & 0xFFFF0000) + | (($t_3[$i] >> 16) & 0x0000FFFF); + static::$tValues[0][$i << 24] = (($t_3[$i] << 24) & 0xFF000000) + | (($t_3[$i] >> 8) & 0x00FFFFFF); + + static::$dtValues[2][$i << 8] = (($dt3[$i] << 8) & 0xFFFFFF00) + | (($dt3[$i] >> 24) & 0x000000FF); + static::$dtValues[1][$i << 16] = (($dt3[$i] << 16) & 0xFFFF0000) + | (($dt3[$i] >> 16) & 0x0000FFFF); + static::$dtValues[0][$i << 24] = (($dt3[$i] << 24) & 0xFF000000) + | (($dt3[$i] >> 8) & 0x00FFFFFF); + + static::$sboxes[1][$i << 8] = static::$sboxes[0][$i] << 8; + static::$sboxes[2][$i << 16] = static::$sboxes[0][$i] << 16; + static::$sboxes[3][$i << 24] = static::$sboxes[0][$i] << 24; + static::$invSBoxes[1][$i << 8] = static::$invSBoxes[0][$i] << 8; + static::$invSBoxes[2][$i << 16] = static::$invSBoxes[0][$i] << 16; + static::$invSBoxes[3][$i << 24] = static::$invSBoxes[0][$i] << 24; + } + } + } + + /** + * Construct the instance for the supplied cipher name + * + * @param string $cipher The cipher to implement + * + * @return void + * @throws InvalidArgumentException if the cipher is not supported + */ + public function __construct($cipher) { + parent::__construct($cipher); + list (, $bits) = explode('-', $cipher, 2); + $this->setBlockSize($bits); + $this->setKeySize($bits); + static::init(); + } + + /** + * Set the key to use for the cipher + * + * @param string $key The key to use + * + * @throws InvalidArgumentException If the key is not the correct size + * @return void + */ + public function setKey($key) { + $length = strlen($key); + if ($length != $this->keySize) { + $this->setKeySize($length << 3); + if ($length != $this->keySize) { + throw new \InvalidArgumentException( + 'The key is not of a supported length' + ); + } + } + $this->key = $key; + $this->initialized = $this->initialize(); + } + + /** + * Decrypt a block of data using the supplied string key + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to decrypt + * + * @return string The result decrypted data + */ + protected function decryptBlockData($data) { + $schedule = $this->decryptionSchedule; + return $this->decryptBlockPart($data, $schedule); + } + + /** + * Encrypt a block of data using the supplied string key + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to encrypt + * + * @return string The result encrypted data + */ + protected function encryptBlockData($data) { + $schedule = $this->encryptionSchedule; + return $this->encryptBlockPart($data, $schedule); + } + + /** + * Initialize the cipher by preparing the key + * + * @return boolean The status of the initialization + */ + protected function initialize() { + $this->encryptionSchedule = $this->getEncryptionSchedule($this->key); + $this->decryptionSchedule = $this->getDecryptionSchedule($this->key); + return true; + } + + /** + * Encrypt a piece of the block (a single block sized chunk) + * + * @param string $part The block part to encrypt + * @param array $schedule The key schedule to use for encryption + * + * @return string The encrypted string + */ + protected function encryptBlockPart($part, array $schedule) { + $words = unpack('N*words', $part); + $state = array(); + $ictr = 0; + foreach ($words as $word) { + $state[] = $word ^ $schedule[0][$ictr++]; + } + $nBlocks = $this->blockSize >> 2; + $nKeys = $this->keySize >> 2; + $nRounds = max($nBlocks, $nKeys) + 6; + $temp = array(); + for ($round = 1; $round < $nRounds; $round++) { + $ictr = $this->shiftOffsets[0]; + $jctr = $this->shiftOffsets[1]; + $kctr = $this->shiftOffsets[2]; + $lctr = $this->shiftOffsets[3]; + while ($ictr < $nBlocks) { + $temp[$ictr] = static::$tValues[0][$state[$ictr] & 0xFF000000] + ^ static::$tValues[1][$state[$jctr] & 0x00FF0000] + ^ static::$tValues[2][$state[$kctr] & 0x0000FF00] + ^ static::$tValues[3][$state[$lctr] & 0x000000FF] + ^ $schedule[$round][$ictr]; + $ictr++; + $jctr = ($jctr + 1) % $nBlocks; + $kctr = ($kctr + 1) % $nBlocks; + $lctr = ($lctr + 1) % $nBlocks; + } + for ($i = 0; $i < $nBlocks; $i++) { + $state[$i] = $temp[$i]; + } + } + for ($i = 0; $i < $nBlocks; $i++) { + $state[$i] = $this->subWord($state[$i]); + } + $ictr = $this->shiftOffsets[0]; + $jctr = $this->shiftOffsets[1]; + $kctr = $this->shiftOffsets[2]; + $lctr = $this->shiftOffsets[3]; + while ($ictr < $nBlocks) { + $temp[$ictr] = ($state[$ictr] & 0xFF000000) + ^ ($state[$jctr] & 0x00FF0000) + ^ ($state[$kctr] & 0x0000FF00) + ^ ($state[$lctr] & 0x000000FF) + ^ $schedule[$nRounds][$ictr]; + $ictr++; + $jctr = ($jctr + 1) % $nBlocks; + $kctr = ($kctr + 1) % $nBlocks; + $lctr = ($lctr + 1) % $nBlocks; + } + $state = $temp; + array_unshift($state, 'N*'); + return call_user_func_array('pack', $state); + } + + /** + * Decrypt a piece of the block (a single block sized chunk) + * + * @param string $part The block part to decrypt + * @param array $schedule The key schedule to use for decryption + * + * @return string The decrypted string + */ + protected function decryptBlockPart($part, $schedule) { + $state = array(); + $words = unpack('N*word', $part); + $inc = 0; + $nBlocks = $this->blockSize >> 2; + $nKeys = $this->keySize >> 2; + $nRounds = max($nBlocks, $nKeys) + 6; + foreach ($words as $word) { + $state[] = $word ^ $schedule[$nRounds][$inc++]; + } + $temp = array(); + for ($round = $nRounds - 1; $round > 0; $round--) { + $ictr = $this->shiftOffsets[0]; + $jctr = $nBlocks - $this->shiftOffsets[1]; + $kctr = $nBlocks - $this->shiftOffsets[2]; + $lctr = $nBlocks - $this->shiftOffsets[3]; + + while ($ictr < $nBlocks) { + $temp[$ictr] = static::$dtValues[0][$state[$ictr] & 0xFF000000] + ^ static::$dtValues[1][$state[$jctr] & 0x00FF0000] + ^ static::$dtValues[2][$state[$kctr] & 0x0000FF00] + ^ static::$dtValues[3][$state[$lctr] & 0x000000FF] + ^ $schedule[$round][$ictr]; + $ictr++; + $jctr = ($jctr + 1) % $nBlocks; + $kctr = ($kctr + 1) % $nBlocks; + $lctr = ($lctr + 1) % $nBlocks; + } + for ($i = 0; $i < $nBlocks; $i++) { + $state[$i] = $temp[$i]; + } + } + $ictr = $this->shiftOffsets[0]; + $jctr = $nBlocks - $this->shiftOffsets[1]; + $kctr = $nBlocks - $this->shiftOffsets[2]; + $lctr = $nBlocks - $this->shiftOffsets[3]; + $temp = array(); + while ($ictr < $nBlocks) { + $temp[$ictr] = $schedule[0][$ictr] + ^ $this->invSubWord( + ($state[$ictr] & 0xFF000000) + | ($state[$jctr] & 0x00FF0000) + | ($state[$kctr] & 0x0000FF00) + | ($state[$lctr] & 0x000000FF) + ); + $ictr++; + $jctr = ($jctr + 1) % $nBlocks; + $kctr = ($kctr + 1) % $nBlocks; + $lctr = ($lctr + 1) % $nBlocks; + } + $state = $temp; + array_unshift($state, 'N*'); + return call_user_func_array('pack', $state); + } + + /** + * Set the block size to use for the cipher + * + * @param int $num The number of bits in the block size + * + * @return void + */ + protected function setBlockSize($num) { + $num >>= 5; + $num = max(min($num, 8), 4); + $this->blockSize = $num << 2; + } + + /** + * Set the key size to use for the cipher + * + * @param int $num The number of bits in the key size + * + * @return void + */ + protected function setKeySize($num) { + $num >>= 5; + $num = max(min($num, 8), 4); + $this->keySize = $num << 2; + } + + /** + * Setup the cipher by determining the shift offsets, the key size and + * precomputing part of the key schedule + * + * @param string $key The key to setup the cipher for + * + * @return array The precomputed schedule part + */ + protected function setup($key) { + $this->setShiftOffsets(); + $nBlocks = $this->blockSize >> 2; + $nKeys = $this->keySize >> 2; + $nRounds = max($nBlocks, $nKeys) + 6; + $words = array_values(unpack('N*words', $key)); + $length = $nBlocks * ($nRounds + 1); + for ($i = $nKeys; $i < $length; $i++) { + $temp = $words[$i - 1]; + if ($i % $nKeys == 0) { + $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); + $temp = $this->subWord($temp) ^ static::$rcon[$i / $nKeys]; + } elseif ($nKeys > 6 && ($i % $nKeys == 4)) { + $temp = $this->subWord($temp); + } + $words[$i] = $words[$i - $nKeys] ^ $temp; + } + return $words; + } + + /** + * Convert the kye into an encryption schedule + * + * @param string $key The key to use + * + * @return array The generated key schedule + */ + protected function getEncryptionSchedule($key) { + $words = $this->setup($key); + $nBlocks = $this->blockSize >> 2; + $length = $nBlocks * (max($nBlocks, $this->keySize >> 2) + 7); + $schedule = array(); + for ($i = $row = $col = 0; $i < $length; $i++, $col++) { + if ($col == $nBlocks) { + $col = 0; + $row++; + } + if (!isset($schedule[$row])) { + $schedule[$row] = array(); + } + $schedule[$row][$col] = $words[$i]; + } + return $schedule; + } + + /** + * Convert the kye into an decryption schedule + * + * @param string $key The key to use + * + * @return array The generated key schedule + */ + protected function getDecryptionSchedule($key) { + $words = $this->getEncryptionSchedule($key); + $schedule = array(); + $length = count($words) - 1; + $schedule[] = $words[0]; + $nBlocks = $this->blockSize >> 2; + for ($i = 1; $i < $length; $i++) { + $jctr = 0; + $temp = array(); + while ($jctr < $nBlocks) { + $dwblock = $this->subWord($words[$i][$jctr]); + $temp[$jctr] = static::$dtValues[0][$dwblock & 0xFF000000] + ^ static::$dtValues[1][$dwblock & 0x00FF0000] + ^ static::$dtValues[2][$dwblock & 0x0000FF00] + ^ static::$dtValues[3][$dwblock & 0x000000FF]; + $jctr++; + } + $schedule[$i] = $temp; + } + $schedule[] = $words[$length]; + return $schedule; + } + + /** + * Compute the word by merging it with the sboxes + * + * @param string $word + * + * @return string The computed word + */ + protected function subWord($word) { + return static::$sboxes[0][$word & 0x000000FF] + | static::$sboxes[1][$word & 0x0000FF00] + | static::$sboxes[2][$word & 0x00FF0000] + | static::$sboxes[3][$word & 0xFF000000]; + } + + /** + * Compute the word by merging it with the inverse sboxes + * + * @param string $word + * + * @return string The computed word + */ + protected function invSubWord($word) { + return static::$invSBoxes[0][$word & 0x000000FF] + | static::$invSBoxes[1][$word & 0x0000FF00] + | static::$invSBoxes[2][$word & 0x00FF0000] + | static::$invSBoxes[3][$word & 0xFF000000]; + } + + /** + * Setup the shift offsets to use for the cipher + * + * @return void + */ + protected function setShiftOffsets() { + switch ($this->blockSize >> 2) { + case 4: + case 5: + case 6: + $this->shiftOffsets = array(0, 1, 2, 3); + break; + case 7: + $this->shiftOffsets = array(0, 1, 2, 4); + break; + case 8: + $this->shiftOffsets = array(0, 1, 3, 4); + } + } + +} diff --git a/CryptLib/Cipher/Block/Cipher/TripleDES.php b/CryptLib/Cipher/Block/Cipher/TripleDES.php new file mode 100644 index 0000000..48ac826 --- /dev/null +++ b/CryptLib/Cipher/Block/Cipher/TripleDES.php @@ -0,0 +1,116 @@ + + * @author Jim Wigginton + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Cipher; + +/** + * An implementation of the TripleDES Cipher + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ +class TripleDES extends DES { + + /** + * @var int The key size for the cipher + */ + protected $keySize = 24; + + /** + * Get a list of supported ciphers for this class implementation + * + * @return array A list of supported ciphers + */ + public static function getSupportedCiphers() { + return array('tripledes'); + } + + /** + * Set the key to use for the cipher + * + * @param string $key The key to use + * + * @throws InvalidArgumentException If the key is not the correct size + * @return void + */ + public function setKey($key) { + $len = strlen($key); + if ($len == 16) { + $key .= substr($key, 0, 8); + } elseif ($len != 24) { + throw new \InvalidArgumentException( + 'The supplied key block is not the correct size' + ); + } + $this->key = $key; + $this->initialized = true; + } + + /** + * Decrypt a block of data using the supplied string key + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to decrypt + * + * @return string The result decrypted data + */ + protected function decryptBlockData($data) { + $key = $this->key; + $this->key = substr($key, 16, 8); + $this->initialize(); + $data = parent::decryptBlockData($data); + $this->key = substr($key, 8, 8); + $this->initialize(); + $data = parent::encryptBlockData($data); + $this->key = substr($key, 0, 8); + $this->initialize(); + $data = parent::decryptBlockData($data); + $this->key = $key; + return $data; + } + + /** + * Encrypt a block of data using the supplied string key + * + * Note that the supplied data should be the same size as the block size of + * the cipher being used. + * + * @param string $data The data to encrypt + * + * @return string The result encrypted data + */ + protected function encryptBlockData($data) { + $key = $this->key; + $this->key = substr($key, 0, 8); + $this->initialize(); + $data = parent::encryptBlockData($data); + $this->key = substr($key, 8, 8); + $this->initialize(); + $data = parent::decryptBlockData($data); + $this->key = substr($key, 16, 8); + $this->initialize(); + $data = parent::encryptBlockData($data); + $this->key = $key; + return $data; + } + +} diff --git a/CryptLib/Cipher/Block/Mode.php b/CryptLib/Cipher/Block/Mode.php new file mode 100644 index 0000000..4849499 --- /dev/null +++ b/CryptLib/Cipher/Block/Mode.php @@ -0,0 +1,81 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block; + +/** + * The interface that all block cipher modes must implement + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +interface Mode { + + /** + * Build the instance of the cipher mode + * + * @param Cipher $cipher The cipher to use for encryption/decryption + * @param string $initv The initialization vector (empty if not needed) + * @param array $options An array of mode-specific options + */ + public function __construct( + \CryptLib\Cipher\Block\Cipher $cipher, + $initv, + array $options = array() + ); + + /** + * Decrypt the data using the supplied key, cipher and initialization vector + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + public function decrypt($data); + + /** + * Encrypt the data using the supplied key, cipher and initialization vector + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + public function encrypt($data); + + /** + * Finish the mode and append any additional data necessary + * + * @return string Any additional data + */ + public function finish(); + + /** + * Get the name of the current mode implementation + * + * @return string The current mode name + */ + public function getMode(); + + /** + * Reset the mode to start over (destroying any intermediate state) + * + * @return void + */ + public function reset(); + +} diff --git a/CryptLib/Cipher/Block/Mode/CBC.php b/CryptLib/Cipher/Block/Mode/CBC.php new file mode 100644 index 0000000..a8d3a62 --- /dev/null +++ b/CryptLib/Cipher/Block/Mode/CBC.php @@ -0,0 +1,56 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Mode; + +/** + * The CBC (Cipher Block Chaining) mode implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ + +class CBC extends \CryptLib\Cipher\Block\AbstractMode { + + /** + * Decrypt the data using the supplied key, cipher + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + protected function decryptBlock($data) { + $stub = $this->cipher->decryptBlock($data); + $result = $stub ^ $this->state; + $this->state = $data; + return $result; + } + + /** + * Encrypt the data using the supplied key, cipher + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + protected function encryptBlock($data) { + $stub = $this->cipher->encryptBlock($data ^ $this->state); + $this->state = $stub; + return $stub; + } + +} diff --git a/CryptLib/Cipher/Block/Mode/CCM.php b/CryptLib/Cipher/Block/Mode/CCM.php new file mode 100644 index 0000000..5bffa7e --- /dev/null +++ b/CryptLib/Cipher/Block/Mode/CCM.php @@ -0,0 +1,374 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + * @see http://tools.ietf.org/html/rfc3610 + */ + +namespace CryptLib\Cipher\Block\Mode; + +/** + * The CCM (Counter CBC-MAC) mode implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + * @see http://tools.ietf.org/html/rfc3610 + */ +class CCM extends \CryptLib\Cipher\Block\AbstractMode { + + /** + * Indicates which mode the cipher mode is in (encryption/decryption) + */ + const MODE_DECRYPT = 1; + + /** + * Indicates which mode the cipher mode is in (encryption/decryption) + */ + const MODE_ENCRYPT = 2; + + /** + * @var int The number of octets in the Authentication field + */ + protected $authFieldSize = 8; + + /** + * @var string The data buffer for this encryption instance + */ + protected $data = ''; + + /** + * @var int The number of octets in the length field + */ + protected $lSize = 4; + + /** + * @var string The mode name for the current instance + */ + protected $mode = 'ccm'; + + /** + * @var int The current encryption mode (enc/dec) + */ + protected $encryptionMode = 0; + + /** + * @var array Mode specific options + */ + protected $options = array( + 'adata' => '', + 'lSize' => 4, + 'aSize' => 8, + ); + + /** + * @var string The initialization vector to use for this instance + */ + protected $usedIV = ''; + + /** + * Build the instance of the cipher mode + * + * @param Cipher $cipher The cipher to use for encryption/decryption + * @param string $initv The initialization vector (empty if not needed) + * @param array $options An array of mode-specific options + */ + public function __construct( + \CryptLib\Cipher\Block\Cipher $cipher, + $initv, + array $options = array() + ) { + $this->options = $options + $this->options; + $this->cipher = $cipher; + $this->initv = $initv; + $this->adata = $this->options['adata']; + $this->setLSize($this->options['lSize']); + $this->setAuthFieldSize($this->options['aSize']); + $this->reset(); + } + + /** + * Finish the mode and append any additional data necessary + * + * @return string Any additional data + */ + public function finish() { + $mask = (static::MODE_DECRYPT | static::MODE_ENCRYPT); + if (!($this->encryptionMode ^ $mask)) { + throw new \LogicException('Cannot encrypt and decrypt in same state'); + } + if ($this->encryptionMode & static::MODE_DECRYPT) { + return $this->decryptBlockFinal(); + } else { + return $this->encryptBlockFinal(); + } + } + + /** + * Set the auth field size to a different value. + * + * Valid values: 4, 6, 8, 10, 12, 14, 16 + * + * Note that increasing this size will make it harder for an attacker to + * modify the message payload + * + * @param int $new The new size of auth field to append + * + * @return void + * @throws InvalidArgumentException If the number is outside of the range + */ + public function setAuthFieldSize($new) { + if (!in_array($new, array(4, 6, 8, 10, 12, 14, 16))) { + throw new \InvalidArgumentException( + 'The Auth Field must be one of: 4, 6, 8, 10, 12, 14, 16' + ); + } + $this->authFieldSize = (int) $new; + $this->reset(); + } + + /** + * Set the size of the length field. This is a tradeoff between the maximum + * message size and the size of the initialization vector + * + * Valid values are 2, 3, 4, 5, 6, 7, 8 + * + * @param int $new The new LSize to use + * + * @return void + * @throws InvalidArgumentException If the number is outside of the range + */ + public function setLSize($new) { + if ($new < 2 || $new > 8) { + throw new \InvalidArgumentException( + 'The LSize must be between 2 and 8 inclusive' + ); + } + $this->lSize = (int) $new; + $this->reset(); + } + + /** + * Reset the mode to start over (destroying any intermediate state) + * + * @return void + */ + public function reset() { + $this->usedIV = $this->extractInitv( + $this->initv, + $this->cipher->getBlockSize() + ); + $this->encryptionMode = 0; + $this->data = ''; + } + + /** + * Decrypt the data using the supplied key, cipher + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + protected function decryptBlock($data) { + $this->data .= $data; + $this->encryptionMode |= static::MODE_DECRYPT; + } + + /** + * Perform the decryption of the block data + * + * @return string The final data + */ + protected function decryptBlockFinal() { + $message = substr($this->data, 0, -1 * $this->authFieldSize); + $uValue = substr($this->data, -1 * $this->authFieldSize); + $data = $this->encryptMessage($message, $uValue); + $computedT = substr($data, -1 * $this->authFieldSize); + $data = substr($data, 0, -1 * $this->authFieldSize); + $authFieldT = $this->computeAuthField($data); + if ($authFieldT != $computedT) { + return false; + } + return rtrim($data, chr(0)); + } + + /** + * Encrypt the data using the supplied key, cipher + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + protected function encryptBlock($data) { + $this->data .= $data; + $this->encryptionMode |= static::MODE_ENCRYPT; + } + + /** + * Perform the encryption of the block data + * + * @return string The final data + */ + protected function encryptBlockFinal() { + $authFieldT = $this->computeAuthField($this->data); + $data = $this->encryptMessage($this->data, $authFieldT); + return $data; + } + + /** + * Compute the authentication field + * + * @param string $data The data to compute with + * + * @return string The computed MAC Authentication Code + */ + protected function computeAuthField($data) { + $blockSize = $this->cipher->getBlockSize(); + $flags = pack( + 'C', + 64 * (empty($this->adata) ? 0 : 1) + + 8 * (($this->authFieldSize - 2) / 2) + + ($this->lSize - 1) + ); + $blocks = array( + $flags . $this->usedIV . pack($this->getLPackString(), strlen($data)) + ); + if (strlen($data) % $blockSize != 0) { + $data .= str_repeat(chr(0), $blockSize - (strlen($data) % $blockSize)); + } + + $blocks = array_merge( + $blocks, + $this->processAData($this->adata, $blockSize) + ); + if (!empty($data)) { + $blocks = array_merge($blocks, str_split($data, $blockSize)); + } + $crypted = array( + 1 => $this->cipher->encryptBlock($blocks[0]) + ); + + $blockLen = count($blocks); + for ($i = 1; $i < $blockLen; $i++) { + $crypted[$i + 1] = $this->cipher->encryptBlock( + $crypted[$i] ^ $blocks[$i] + ); + } + return substr(end($crypted), 0, $this->authFieldSize); + } + + /** + * Encrypt the data using the supplied method + * + * @param string $data The data to encrypt + * @param string $authValue The auth value field + * + * @return string The encrypted data with authfield payload + */ + protected function encryptMessage($data, $authValue) { + $blockSize = $this->cipher->getBlockSize(); + $flags = pack('C', ($this->lSize - 1)); + $blocks = str_split($data, $blockSize); + $sblocks = array(); + $blockLen = count($blocks); + for ($i = 0; $i <= $blockLen; $i++) { + $sblocks[] = $this->cipher->encryptBlock( + $flags . $this->usedIV . pack($this->getLPackString(), $i) + ); + } + $encrypted = ''; + foreach ($blocks as $key => $value) { + if (strlen($value) < $blockSize) { + $sblocks[$key + 1] = substr($sblocks[$key + 1], 0, strlen($value)); + } + $encrypted .= $sblocks[$key + 1] ^ $value; + } + $sValue = substr($sblocks[0], 0, $this->authFieldSize); + $uValue = $authValue ^ $sValue; + return $encrypted . $uValue; + } + + /** + * Enforce the data block is the correct size for the cipher + * + * @param string $data The data to check + * + * @return void + * @throws InvalidArgumentException if the block size is not correct + */ + protected function enforceBlockSize($data) { + return true; + } + + /** + * Extract the nonce from the initialization vector + * + * @param string $initv The initialization Vector to trim + * @param int $blockSize The size of the final nonce + * + * @return string The sized nonce + * @throws InvalidArgumentException if the IV is too short + */ + protected function extractInitv($initv, $blockSize) { + $initSize = $blockSize - 1 - $this->lSize; + if (strlen($initv) < $initSize) { + throw new \InvalidArgumentException(sprintf( + 'Supplied Initialization Vector is too short, should be %d bytes', + $initSize + )); + } + return substr($initv, 0, $initSize); + } + + /** + * Get a packing string related to the instance lSize variable + * + * @return string The pack() string to use to pack the length variables + * @see pack() + */ + protected function getLPackString() { + if ($this->lSize <= 3) { + return str_repeat('x', $this->lSize - 2) . 'n'; + } + return str_repeat('x', $this->lSize - 4) . 'N'; + } + + /** + * Process the Authentication data for authenticating + * + * @param string $adata The data to authenticate with + * @param int $blockSize The block size for the cipher + * + * @return array An array of strings bound by the supplied blocksize + */ + protected function processAData($adata, $blockSize) { + if (!empty($this->adata)) { + if (strlen($this->adata) < ((1 << 16) - (1 << 8))) { + $len = pack('n', strlen($this->adata)); + } else { + $len = chr(0xff) . chr(0xfe) . pack('N', strlen($this->adata)); + } + $temp = $len . $this->adata; + if (strlen($temp) % $blockSize != 0) { + //Pad the string to exactly mod16 + $temp .= str_repeat( + chr(0), + $blockSize - (strlen($temp) % $blockSize) + ); + } + return str_split($temp, $blockSize); + } + return array(); + } + +} diff --git a/CryptLib/Cipher/Block/Mode/CFB.php b/CryptLib/Cipher/Block/Mode/CFB.php new file mode 100644 index 0000000..ddccc61 --- /dev/null +++ b/CryptLib/Cipher/Block/Mode/CFB.php @@ -0,0 +1,55 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Mode; + +/** + * The CFB (Cipher FeedBack) mode implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ +class CFB extends \CryptLib\Cipher\Block\AbstractMode { + + /** + * Decrypt the data using the supplied key, cipher + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + protected function decryptBlock($data) { + $stub = $this->cipher->encryptBlock($this->state); + $rawData = $stub ^ $data; + $this->state = $rawData; + return $rawData; + } + + /** + * Encrypt the data using the supplied key, cipher + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + protected function encryptBlock($data) { + $stub = $this->cipher->encryptBlock($this->state); + $this->state = $data; + return $stub ^ $data; + } + +} diff --git a/CryptLib/Cipher/Block/Mode/CTR.php b/CryptLib/Cipher/Block/Mode/CTR.php new file mode 100644 index 0000000..8c17e86 --- /dev/null +++ b/CryptLib/Cipher/Block/Mode/CTR.php @@ -0,0 +1,94 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Mode; + +use CryptLib\Core\BaseConverter; +use CryptLib\Core\BigMath; + +/** + * The CTR (Counter) mode implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ + +class CTR extends \CryptLib\Cipher\Block\AbstractMode { + + /** + * @var BigMath An instance of the BigMath library + */ + protected $bigMath = null; + + /** + * Build the instance of the cipher mode + * + * @param Cipher $cipher The cipher to use for encryption/decryption + * @param string $initv The initialization vector (empty if not needed) + * @param array $options An array of mode-specific options + */ + public function __construct( + \CryptLib\Cipher\Block\Cipher $cipher, + $initv, + array $options = array() + ) { + parent::__construct($cipher, $initv, $options); + $this->bigMath = BigMath::createFromServerConfiguration(); + } + + /** + * Reset the mode to start over (destroying any intermediate state) + * + * @return void + */ + public function reset() { + $this->state = BaseConverter::ConvertFromBinary($this->initv, '0123456789'); + $this->state = ltrim($this->state, '0'); + } + + /** + * Decrypt the data using the supplied key, cipher + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + protected function decryptBlock($data) { + return $this->encryptBlock($data); + } + + /** + * Encrypt the data using the supplied key, cipher + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + protected function encryptBlock($data) { + $size = $this->cipher->getBlockSize(); + $state = str_pad( + BaseConverter::convertToBinary($this->state, '0123456789'), + $size, + chr(0), + STR_PAD_LEFT + ); + $stub = $this->cipher->encryptBlock($state); + $this->state = $this->bigMath->add($this->state, 1); + return $stub ^ $data; + } + +} diff --git a/CryptLib/Cipher/Block/Mode/ECB.php b/CryptLib/Cipher/Block/Mode/ECB.php new file mode 100644 index 0000000..6adcb1f --- /dev/null +++ b/CryptLib/Cipher/Block/Mode/ECB.php @@ -0,0 +1,50 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Mode; + +/** + * The ECB (Electronic CodeBook) mode implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ +class ECB extends \CryptLib\Cipher\Block\AbstractMode { + + /** + * Decrypt the data using the supplied key, cipher and initialization vector + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + protected function decryptBlock($data) { + return $this->cipher->decryptBlock($data); + } + + /** + * Encrypt the data using the supplied key, cipher and initialization vector + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + protected function encryptBlock($data) { + return $this->cipher->encryptBlock($data); + } + +} diff --git a/CryptLib/Cipher/Block/Mode/NOFB.php b/CryptLib/Cipher/Block/Mode/NOFB.php new file mode 100644 index 0000000..3ed2ac0 --- /dev/null +++ b/CryptLib/Cipher/Block/Mode/NOFB.php @@ -0,0 +1,51 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher\Block\Mode; + +/** + * The NOFB (Nbit Output FeedBack) mode implementation + * + * @category PHPCryptLib + * @package Cipher + * @subpackage Block + * @author Anthony Ferrara + */ +class NOFB extends \CryptLib\Cipher\Block\AbstractMode { + + /** + * Decrypt the data using the supplied key, cipher and initialization vector + * + * @param string $data The data to decrypt + * + * @return string The decrypted data + */ + protected function decryptBlock($data) { + return $this->encryptBlock($data); + } + + /** + * Encrypt the data using the supplied key, cipher and initialization vector + * + * @param string $data The data to encrypt + * + * @return string The encrypted data + */ + protected function encryptBlock($data) { + $this->state = $this->cipher->encryptBlock($this->state); + return $this->state ^ $data; + } + +} diff --git a/CryptLib/Cipher/Factory.php b/CryptLib/Cipher/Factory.php new file mode 100644 index 0000000..ec1c512 --- /dev/null +++ b/CryptLib/Cipher/Factory.php @@ -0,0 +1,189 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Cipher; + +/** + * Some used classes, aliased appropriately + */ +use CryptLib\Cipher\Block\Cipher as Cipher; +use CryptLib\Cipher\Block\Mode as Mode; +use CryptLib\Cipher\Block\Cipher\MCrypt; + + +/** + * The Cipher Factory + * + * Use this factory to instantiate ciphers and modes based upon their names. You + * can register new ciphers and modes by simply calling the appropriate methods. + * + * @category PHPCryptLib + * @package Cipher + * @author Anthony Ferrara + */ +class Factory extends \CryptLib\Core\AbstractFactory { + + /** + * @var array A list of available cipher implementations by name of cipher + */ + protected $ciphers = array(); + + /** + * @var array A list of available mode implementations by name of mode + */ + protected $modes = array(); + + /** + * Instantiate the factory + * + * This automatically loads and registers the default cipher and mode + * implementations. + * + * @return void + */ + public function __construct() { + $this->loadModes(); + $this->loadCiphers(); + } + + /** + * Get an instance of a cipher by name + * + * Note that this will return the passed argument if it is an instance of + * the Block cipher interface. + * + * @param string|Block $cipher The cipher name or instance to load + * + * @return Cipher The loaded block cipher + * @throws RuntimeException if the cipher is not supported + */ + public function getBlockCipher($cipher) { + if (is_object($cipher) && $cipher instanceof Cipher) { + return $cipher; + } + $cipher = strtolower($cipher); + if (in_array($cipher, MCrypt::getSupportedCiphers())) { + //Use the built in MCrypt library if it's available + return new MCrypt($cipher); + } elseif (isset($this->ciphers[$cipher])) { + $class = $this->ciphers[$cipher]; + return new $class($cipher); + } + $message = sprintf('Unsupported Cipher %s', $cipher); + throw new \RuntimeException($message); + } + + /** + * Get an instance of a mode by name + * + * Note that this will return the passed argument if it is an instance of + * the Mode interface. + * + * @param string|Mode $mode The mode name or instance to load + * + * @return Mode The loaded mode instance + * @throws RuntimeException if the mode is not supported + */ + public function getMode( + $mode, + \CryptLib\Cipher\Block\Cipher $cipher, + $initv, + array $options = array() + ) { + if (is_object($mode) && $mode instanceof Mode) { + return $mode; + } + $mode = strtolower($mode); + if (isset($this->modes[$mode])) { + $class = $this->modes[$mode]; + return new $class($cipher, $initv, $options); + } + $message = sprintf('Unsupported Mode %s', $mode); + throw new \RuntimeException($message); + } + + /** + * Register a new cipher implementation class for this factory + * + * This will iterate over each supported cipher for the class and load the + * cipher into the list of supported ciphers + * + * @param string $name The name of the cipher (ignored) + * @param string $class The full class name of the cipher implementation + * + * @return Factory $this The current factory instance + * @throws InvalidArgumentException If the class is not a block cipher + */ + public function registerCipher($name, $class) { + $refl = new \ReflectionClass($class); + $interface = '\\'. __NAMESPACE__ . '\\Block\\Cipher'; + if (!$refl->implementsInterface($interface)) { + $message = sprintf('Class must implement %s', $interface); + throw new \InvalidArgumentException($message); + } + foreach ($class::getSupportedCiphers() as $cipher){ + $this->ciphers[$cipher] = $class; + } + return $this; + } + + /** + * Register a new mode implementation class for this factory + * + * @param string $name The name of the mode (ignored) + * @param string $class The full class name of the mode implementation + * + * @return Factory $this The current factory instance + * @throws InvalidArgumentException If the class is not a valid mode + */ + public function registerMode($name, $class) { + $refl = new \ReflectionClass($class); + $interface = '\\'. __NAMESPACE__ . '\\Block\\Mode'; + if (!$refl->implementsInterface($interface)) { + throw new \InvalidArgumentException('Class must implement Mode'); + } + $this->modes[strtolower($name)] = $class; + return $this; + } + + /** + * Load all core cipher implementations + * + * @return void + */ + protected function loadCiphers() { + $this->loadFiles( + __DIR__ . '/Block/Cipher', + __NAMESPACE__ . '\\Block\\Cipher\\', + array($this, 'registerCipher') + ); + } + + /** + * Load all core mode implementations + * + * @return void + */ + protected function loadModes() { + $this->loadFiles( + __DIR__ . '/Block/Mode', + __NAMESPACE__ . '\\Block\\Mode\\', + array($this, 'registerMode') + ); + } + +} diff --git a/CryptLib/Core/AbstractFactory.php b/CryptLib/Core/AbstractFactory.php new file mode 100644 index 0000000..eb7fc48 --- /dev/null +++ b/CryptLib/Core/AbstractFactory.php @@ -0,0 +1,82 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Core; + +/** + * The base abstract factory used by all CryptLib factories + * + * @category PHPCryptLib + * @package Core + * @author Anthony Ferrara + */ +abstract class AbstractFactory { + + /** + * Register a type with the factory by name + * + * This is an internal method to check if a provided class name implements + * an interface, and if it does to append that class to an internal array + * by name. + * + * @param string $type The name of the variable to store the class + * @param string $implements The interface to validate against + * @param string $name The name of this particular class + * @param string $class The fully qualified class name + * @param boolean $instantiate Should the class be stored instantiated + * + * @return void + * @throws InvalidArgumentException If class does not implement interface + */ + protected function registerType( + $type, + $implements, + $name, + $class, + $instantiate = false + ) { + $name = strtolower($name); + $refl = new \ReflectionClass($class); + if (!$refl->implementsInterface($implements)) { + $message = sprintf('Class must implement %s', $implements); + throw new \InvalidArgumentException($message); + } + if ($instantiate) { + $class = new $class; + } + + $this->{$type}[$name] = $class; + } + + /** + * Load a set of classes from a directory into the factory + * + * @param string $directory The directory to search for classes in + * @param string $namespace The namespace prefix for any found classes + * @param string $callback The callback with which to register the class + * + * @return void + */ + protected function loadFiles($directory, $namespace, $callback) { + foreach (new \DirectoryIterator($directory) as $file) { + $filename = $file->getBasename(); + if ($file->isFile() && substr($filename, -4) == '.php') { + $name = substr($filename, 0, -4); + $class = $namespace . $name; + call_user_func($callback, $name, $class); + } + } + } + +} diff --git a/CryptLib/Core/AutoLoader.php b/CryptLib/Core/AutoLoader.php new file mode 100644 index 0000000..ddd769e --- /dev/null +++ b/CryptLib/Core/AutoLoader.php @@ -0,0 +1,92 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Core; + +/** + * An implementation of the PSR-0 Autoloader. This can be replaced at will with + * other implementations if necessary. + * + * @category PHPCryptLib + * @package Core + * @author Anthony Ferrara + */ +class AutoLoader { + + /** + * @var string The namespace prefix for this instance. + */ + protected $namespace = ''; + + /** + * @var string The filesystem prefix to use for this instance + */ + protected $path = ''; + + /** + * Build the instance of the autoloader + * + * @param string $namespace The prefixed namespace this instance will load + * @param string $path The filesystem path to the root of the namespace + * + * @return void + */ + public function __construct($namespace, $path) { + $this->namespace = ltrim($namespace, '\\'); + $this->path = rtrim($path, '/\\') . DIRECTORY_SEPARATOR; + } + + /** + * Try to load a class + * + * @param string $class The class name to load + * + * @return boolean If the loading was successful + */ + public function load($class) { + $class = ltrim($class, '\\'); + if (strpos($class, $this->namespace) === 0) { + $nsparts = explode('\\', $class); + $class = array_pop($nsparts); + $nsparts[] = ''; + $path = $this->path . implode(DIRECTORY_SEPARATOR, $nsparts); + $path .= str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; + if (file_exists($path)) { + require $path; + return true; + } + } + return false; + } + + /** + * Register the autoloader to PHP + * + * @return boolean The status of the registration + */ + public function register() { + return spl_autoload_register(array($this, 'load')); + } + + /** + * Unregister the autoloader to PHP + * + * @return boolean The status of the unregistration + */ + public function unregister() { + return spl_autoload_unregister(array($this, 'load')); + } + +} \ No newline at end of file diff --git a/CryptLib/Core/BaseConverter.php b/CryptLib/Core/BaseConverter.php new file mode 100644 index 0000000..9c086f8 --- /dev/null +++ b/CryptLib/Core/BaseConverter.php @@ -0,0 +1,114 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Core; + +/** + * A Utility class for converting between raw binary strings and a given + * list of characters + * + * @category PHPCryptLib + * @package Core + * @author Anthony Ferrara + */ +class BaseConverter { + + /** + * Convert from a raw binary string to a string of characters + * + * @param string $string The string to convert from + * @param string $characters The list of characters to convert to + * + * @return string The converted string + */ + public static function convertFromBinary($string, $characters) { + if ($string === '' || empty($characters)) { + return ''; + } + $string = str_split($string); + $callback = function($str) { + return ord($str); + }; + $string = array_map($callback, $string); + $converted = static::baseConvert($string, 256, strlen($characters)); + $callback = function ($num) use ($characters) { + return $characters[$num]; + }; + $ret = implode('', array_map($callback, $converted)); + return $ret; + } + + /** + * Convert to a raw binary string from a string of characters + * + * @param string $string The string to convert from + * @param string $characters The list of characters to convert to + * + * @return string The converted string + */ + public static function convertToBinary($string, $characters) { + if (empty($string) || empty($characters)) { + return ''; + } + $string = str_split($string); + $callback = function($str) use ($characters) { + return strpos($characters, $str); + }; + $string = array_map($callback, $string); + $converted = static::baseConvert($string, strlen($characters), 256); + $callback = function ($num) { + return chr($num); + }; + return implode('', array_map($callback, $converted)); + } + + /** + * Convert an array of input blocks to another numeric base + * + * This function was modified from an implementation found on StackOverflow. + * Special Thanks to @KeithRandall for supplying the implementation. + * + * @param int[] $source The source number, as an array + * @param int $srcBase The source base as an integer + * @param int $dstBase The destination base as an integer + * + * @see http://codegolf.stackexchange.com/questions/1620/arb/1626#1626 + * @return int[] An array of integers in the encoded base + */ + public static function baseConvert(array $source, $srcBase, $dstBase) { + if ($dstBase < 2) { + $message = sprintf('Invalid Destination Base: %d', $dstBase); + throw new \InvalidArgumentException($message); + } + $result = array(); + $count = count($source); + while ($count) { + $itMax = $count; + $remainder = $count = $i = 0; + while($i < $itMax) { + $dividend = $source[$i++] + $remainder * $srcBase; + $remainder = $dividend % $dstBase; + $res = ($dividend - $remainder) / $dstBase; + if ($count || $res) { + $source[$count++] = $res; + } + } + $result[] = $remainder; + } + return array_reverse($result); + } + +} diff --git a/CryptLib/Core/BigMath.php b/CryptLib/Core/BigMath.php new file mode 100644 index 0000000..2c09304 --- /dev/null +++ b/CryptLib/Core/BigMath.php @@ -0,0 +1,65 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\Core; + +/** + * A class for arbitrary precision math functions + * + * @category PHPCryptLib + * @package Core + * @author Anthony Ferrara + */ +abstract class BigMath { + + /** + * Get an instance of the big math class + * + * This is NOT a singleton. It simply loads the proper strategy + * given the current server configuration + * + * @return \CryptLib\Core\BigMath A big math instance + */ + public static function createFromServerConfiguration() { + //@codeCoverageIgnoreStart + if (extension_loaded('bcmath')) { + return new \CryptLib\Core\BigMath\BCMath(); + } elseif (extension_loaded('gmp')) { + return new \CryptLib\Core\BigMath\GMP(); + } else { + return new \CryptLib\Core\BigMath\PHPMath(); + } + //@codeCoverageIgnoreEnd + } + + /** + * Add two numbers together + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return A base-10 string of the sum of the two arguments + */ + abstract public function add($left, $right); + + /** + * Subtract two numbers + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return A base-10 string of the difference of the two arguments + */ + abstract public function subtract($left, $right); + +} \ No newline at end of file diff --git a/CryptLib/Core/BigMath/BCMath.php b/CryptLib/Core/BigMath/BCMath.php new file mode 100644 index 0000000..96fee59 --- /dev/null +++ b/CryptLib/Core/BigMath/BCMath.php @@ -0,0 +1,50 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\Core\BigMath; + +/** + * A class for arbitrary precision math functions implemented using bcmath + * + * @category PHPCryptLib + * @package Core + * @subpackage BigMath + */ +class BCMath extends \CryptLib\Core\BigMath { + + /** + * Add two numbers together + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return A base-10 string of the sum of the two arguments + */ + public function add($left, $right) { + return bcadd($left, $right, 0); + } + + /** + * Subtract two numbers + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return A base-10 string of the difference of the two arguments + */ + public function subtract($left, $right) { + return bcsub($left, $right); + } + +} \ No newline at end of file diff --git a/CryptLib/Core/BigMath/GMP.php b/CryptLib/Core/BigMath/GMP.php new file mode 100644 index 0000000..4b8edbe --- /dev/null +++ b/CryptLib/Core/BigMath/GMP.php @@ -0,0 +1,50 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\Core\BigMath; + +/** + * A class for arbitrary precision math functions implemented using GMP + * + * @category PHPCryptLib + * @package Core + * @subpackage BigMath + */ +class GMP extends \CryptLib\Core\BigMath { + + /** + * Add two numbers together + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return A base-10 string of the sum of the two arguments + */ + public function add($left, $right) { + return gmp_strval(gmp_add($left, $right)); + } + + /** + * Subtract two numbers + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return A base-10 string of the difference of the two arguments + */ + public function subtract($left, $right) { + return gmp_strval(gmp_sub($left, $right)); + } + +} \ No newline at end of file diff --git a/CryptLib/Core/BigMath/PHPMath.php b/CryptLib/Core/BigMath/PHPMath.php new file mode 100644 index 0000000..10bb2c5 --- /dev/null +++ b/CryptLib/Core/BigMath/PHPMath.php @@ -0,0 +1,170 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\Core\BigMath; + +use CryptLib\Core\BaseConverter; + +/** + * A class for arbitrary precision math functions implemented in PHP + * + * @category PHPCryptLib + * @package Core + * @subpackage BigMath + */ +class PHPMath extends \CryptLib\Core\BigMath { + + /** + * Add two numbers together + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return A base-10 string of the sum of the two arguments + */ + public function add($left, $right) { + if (empty($left)) { + return $right; + } elseif (empty($right)) { + return $left; + } + $negative = ''; + if ($left[0] == '-' && $right[0] == '-') { + $negative = '-'; + $left = substr($left, 1); + $right = substr($right, 1); + } elseif ($left[0] == '-') { + return $this->subtract($right, substr($left, 1)); + } elseif ($right[0] == '-') { + return $this->subtract($left, substr($right, 1)); + } + $left = $this->normalize($left); + $right = $this->normalize($right); + $result = BaseConverter::convertFromBinary( + $this->addBinary($left, $right), + '0123456789' + ); + return $negative . $result; + } + + /** + * Subtract two numbers + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return A base-10 string of the difference of the two arguments + */ + public function subtract($left, $right) { + if (empty($left)) { + return $right; + } elseif (empty($right)) { + return $left; + } elseif ($right[0] == '-') { + return $this->add($left, substr($right, 1)); + } elseif ($left[0] == '-') { + return '-' . $this->add(ltrim($left, '-'), $right); + } + $left = $this->normalize($left); + $right = $this->normalize($right); + $results = $this->subtractBinary($left, $right); + $result = BaseConverter::convertFromBinary($results[1], '0123456789'); + return $results[0] . $result; + } + + /** + * Add two binary strings together + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return string The binary result + */ + protected function addBinary($left, $right) { + $len = max(strlen($left), strlen($right)); + $left = str_pad($left, $len, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $len, chr(0), STR_PAD_LEFT); + $result = ''; + $carry = 0; + for ($i = 0; $i < $len; $i++) { + $sum = ord($left[$len - $i - 1]) + + ord($right[$len - $i - 1]) + + $carry; + $result .= chr($sum % 256); + $carry = $sum >> 8; + } + while ($carry) { + $result .= chr($carry % 256); + $carry >>= 8; + } + return strrev($result); + } + + /** + * Subtract two binary strings using 256's compliment + * + * @param string $left The left argument + * @param string $right The right argument + * + * @return string The binary result + */ + protected function subtractBinary($left, $right) { + $len = max(strlen($left), strlen($right)); + $left = str_pad($left, $len, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $len, chr(0), STR_PAD_LEFT); + $right = $this->compliment($right); + $result = $this->addBinary($left, $right); + if (strlen($result) > $len) { + // Positive Result + $carry = substr($result, 0, -1 * $len); + $result = substr($result, strlen($carry)); + return array( + '', + $this->addBinary($result, $carry) + ); + } + return array('-', $this->compliment($result)); + } + + /** + * Take the 256 base compliment + * + * @param string $string The binary string to compliment + * + * @return string The complimented string + */ + protected function compliment($string) { + $result = ''; + $len = strlen($string); + for ($i = 0; $i < $len; $i++) { + $result .= chr(255 - ord($string[$i])); + } + return $result; + } + + /** + * Transform a string number into a binary string using base autodetection + * + * @param string $string The string to transform + * + * @return string The binary transformed number + */ + protected function normalize($string) { + return BaseConverter::convertToBinary( + $string, + '0123456789' + ); + } + +} \ No newline at end of file diff --git a/CryptLib/Core/Enum.php b/CryptLib/Core/Enum.php new file mode 100644 index 0000000..13f413c --- /dev/null +++ b/CryptLib/Core/Enum.php @@ -0,0 +1,117 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\Core; + +use \ReflectionObject; + +/** + * The Enum base class for Enum functionality + * + * This is based off of the SplEnum class implementation (which is only available + * as a PECL extension in 5.3) + * + * @see http://www.php.net/manual/en/class.splenum.php + * @category PHPCryptLib + * @package Core + * @author Anthony Ferrara + */ +abstract class Enum { + + /** + * A default value of null is provided. Override this to set your own default + */ + const __DEFAULT = null; + + /** + * @var string The name of the constant this instance is using + */ + protected $name = ''; + + /** + * @var scalar The value of the constant this instance is using. + */ + protected $value = ''; + + /** + * Creates a new value of the Enum type + * + * @param mixed $value The value this instance represents + * @param boolean $strict Not Implemented at this time + * + * @return void + * @throws UnexpectedValueException If the value is not a constant + */ + public function __construct($value = null, $strict = false) { + if (is_null($value)) { + $value = static::__DEFAULT; + } + $validValues = $this->getConstList(); + $this->name = array_search($value, $validValues); + if (!$this->name) { + throw new \UnexpectedValueException( + 'Value not a const in enum ' . get_class($this) + ); + } + $this->value = $value; + } + + /** + * Cast the current object to a string and return its value + * + * @return mixed the current value of the instance + */ + public function __toString() { + return (string) $this->value; + } + + /** + * Compare two enums using numeric comparison + * + * @param Enum $arg The enum to compare this instance to + * + * @return int 0 if same, 1 if the argument is greater, -1 else + */ + public function compare(Enum $arg) { + if ($this->value == $arg->value) { + return 0; + } elseif ($this->value > $arg->value) { + return -1; + } else { + return 1; + } + } + + /** + * Returns all constants (including values) as an associative array + * + * @param boolean $include_default Include the __default magic value? + * + * @return array All of the constants found against this instance + */ + public function getConstList($include_default = false) { + static $constCache = array(); + $class = get_class($this); + if (!isset($constCache[$class])) { + $reflector = new ReflectionObject($this); + $constCache[$class] = $reflector->getConstants(); + } + if (!$include_default) { + $constants = $constCache[$class]; + unset($constants['__DEFAULT']); + return $constants; + } + return $constCache[$class]; + } + +} \ No newline at end of file diff --git a/CryptLib/Core/Strength.php b/CryptLib/Core/Strength.php new file mode 100644 index 0000000..60c5372 --- /dev/null +++ b/CryptLib/Core/Strength.php @@ -0,0 +1,59 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Core; + +/** + * The strength FlyweightEnum class + * + * All mixing strategies must extend this class + * + * @category PHPCryptLib + * @package Core + * @author Anthony Ferrara + */ +class Strength extends Enum { + + /** + * We provide a default value of VeryLow so that we don't accidentally over + * state the strength if we forget to pass in a value... + */ + const __DEFAULT = self::VERYLOW; + + /** + * This represents Non-Cryptographic strengths. It should not be used any time + * that security or confidentiality is at stake + */ + const VERYLOW = 1; + + /** + * This represents the bottom line of Cryptographic strengths. It may be used + * for low security uses where some strength is required. + */ + const LOW = 3; + + /** + * This is the general purpose Cryptographical strength. It should be suitable + * for all uses except the most sensitive. + */ + const MEDIUM = 5; + + /** + * This is the highest strength available. It should not be used unless the + * high strength is needed, due to hardware constraints (and entropy + * limitations). + */ + const HIGH = 7; + +} diff --git a/CryptLib/CryptLib.php b/CryptLib/CryptLib.php new file mode 100644 index 0000000..6110450 --- /dev/null +++ b/CryptLib/CryptLib.php @@ -0,0 +1,164 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib; + +/** + * The autoloader class will be autoloaded at this point even if another autoloader + * is in use. So if it does not exist at this point, we know we must bootstrap + * the libraries. + */ +if (!class_exists('\\CryptLib\Core\AutoLoader', true)) { + require_once 'bootstrap.php'; +} + +use CryptLib\Password\Factory as PasswordFactory; +use CryptLib\Random\Factory as RandomFactory; + +/** + * A core wrapper class to provide easy access to some of the cryptographic + * functions contained within the library + * + * @category PHPCryptLib + * @package Core + * @author Anthony Ferrara + */ +class CryptLib { + + /** + * Create a password hash from the supplied password and generator prefix + * + * @param string $password The password to hash + * @param string $prefix The prefix of the hashing function + * + * @return string The generated password hash + */ + public function createPasswordHash($password, $prefix = '$2a$') { + $factory = new PasswordFactory(); + return $factory->createHash($password, $prefix); + } + + /** + * Verify a password against a supplied password hash + * + * @param string $password The supplied password to attempt to verify + * @param string $hash The valid hash to verify against + * + * @return boolean Is the password valid + */ + public function verifyPasswordHash($password, $hash) { + $factory = new PasswordFactory(); + return $factory->verifyHash($password, $hash); + } + + /** + * Get a random element from the array + * + * @param array $sourceArray The source array to fetch from + * + * @return mixed A random element from the source array + */ + public function getRandomArrayElement(array $sourceArray) { + $keys = array_keys($sourceArray); + $upperBound = count($keys); + $factory = new RandomFactory; + $generator = $factory->getMediumStrengthGenerator(); + $key = $generator->generateInt(0, $upperBound - 1); + return $sourceArray[$keys[$key]]; + } + + /** + * Generate a random full-byte string (characters 0 - 255) + * + * @param int $size The length of the generated string + * + * @return string The generated string + */ + public function getRandomBytes($size) { + $factory = new RandomFactory; + $generator = $factory->getMediumStrengthGenerator(); + return $generator->generate($size); + } + + /** + * Get a random number between the supplied boundaries + * + * @param int $min The smallest bound the generated number can be + * @param int $max The upper bound on the generated number + * + * @return int The generated random number + */ + public function getRandomNumber($min = 0, $max = PHP_INT_MAX) { + $factory = new RandomFactory; + $generator = $factory->getMediumStrengthGenerator(); + return $generator->generateInt($min, $max); + } + + /** + * Generate a random token using base64 characters (a-zA-Z0-9./) + * + * @param int $size The number of characters in the generated output + * + * @return string The generated token string + */ + public function getRandomToken($size) { + $factory = new RandomFactory; + $generator = $factory->getMediumStrengthGenerator(); + return $generator->generateString($size); + } + + /** + * Shuffle an array. This will preserve key => value relationships, and return + * a new array that has been randomized in order. + * + * To get keys randomized, simply pass the result through array_values()... + * + * @param array $array The input array to randomize + * + * @return array The suffled array + */ + public function shuffleArray(array $array) { + $factory = new RandomFactory; + $generator = $factory->getMediumStrengthGenerator(); + $result = array(); + $values = array_values($array); + $keys = array_keys($array); + $max = count($array); + for ($i = $max - 1; $i >= 0; $i--) { + $int = $generator->generateInt(0, $i); + $result[$keys[$int]] = $values[$int]; + unset($keys[$int], $values[$int]); + $keys = array_values($keys); + $values = array_values($values); + } + return $result; + } + + /** + * Shuffle a string and return the randomized string + * + * @param string $string The string to randomize + * + * @return string The shuffled string + */ + public function shuffleString($string) { + $factory = new RandomFactory; + $generator = $factory->getMediumStrengthGenerator(); + $array = str_split($string); + $result = $this->shuffleArray($array); + return implode('', $result); + } + +} \ No newline at end of file diff --git a/CryptLib/Encryption/Factory.php b/CryptLib/Encryption/Factory.php new file mode 100644 index 0000000..1197d94 --- /dev/null +++ b/CryptLib/Encryption/Factory.php @@ -0,0 +1,107 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Encryption; + +use CryptLib\Cipher\Factory as CipherFactory; + +/** + * The core Encryption Factory + * + * PHP version 5.3 + * + * @category PHPCryptLib + * @package Encryption + * @author Anthony Ferrara + */ +class Factory extends \CryptLib\Core\AbstractFactory { + + /** + * @var Factory The Cipher Factory to use for this instance + */ + protected $cipherFactory = null; + + /** + * @var array An array of PackingModes available for use + */ + protected $packingModes = array(); + + /** + * Build the instance + * + * @param Factory $factory The Cipher Factory to use for this instance + * + * @return void + */ + public function __construct(CryptoLib\Cipher\Factory $factory = null) { + if (is_null($factory)) { + $factory = new CipherFactory(); + } + $this->cipherFactory = $factory; + $this->loadPackingModes(); + } + + /** + * Get a packing mode by name + * + * @param string|PackingMode $name The name of the packing mode or instance + * + * @return PackingMode The instantiated PackingMode class + * @throws RuntimeException If the mode does not exist + */ + public function getPackingMode($name) { + if (is_object($name) && $name instanceof PackingMode) { + return $name; + } + $name = strtolower($name); + if (isset($this->packingModes[$name])) { + $class = $this->packingModes[$name]; + return new $class; + } + $message = sprintf('Invalid Mode Supplied: %s', $name); + throw new \RuntimeException($message); + } + + /** + * Register a new packing mode by name + * + * @param string $name The name of the packing mode + * @param string $class The class to instantiate for the mode + * + * @return Factory $this The current factory instance + */ + public function registerPackingMode($name, $class) { + $this->registerType( + 'packingModes', + __NAMESPACE__ . '\\PackingMode', + $name, + $class + ); + return $this; + } + + /** + * Load the core packing modes for this instance + * + * @return void + */ + protected function loadPackingModes() { + $this->loadFiles( + __DIR__ . '/PackingMode', + __NAMESPACE__ . '\\PackingMode\\', + array($this, 'registerPackingMode') + ); + } + +} diff --git a/CryptLib/Encryption/PackingMode.php b/CryptLib/Encryption/PackingMode.php new file mode 100644 index 0000000..7254ab5 --- /dev/null +++ b/CryptLib/Encryption/PackingMode.php @@ -0,0 +1,55 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Encryption; + +/** + * The core PackingMode interface. All packingmodes must implement this + * interface + * + * A packing mode is a method for padding a string to a specified size using + * various methods + * + * @category PHPCryptLib + * @package Encryption + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +interface PackingMode { + + /** + * Pad the string to the specified size + * + * @param string $string The string to pad + * @param int $blockSize The size to pad to + * + * @return string The padded string + */ + public function pad($string, $blockSize = 32); + + /** + * Strip the padding from the supplied string + * + * @param string $string The string to trim + * + * @return string The unpadded string + */ + public function strip($string); + +} + diff --git a/CryptLib/Encryption/PackingMode/ANSIx923.php b/CryptLib/Encryption/PackingMode/ANSIx923.php new file mode 100644 index 0000000..8e5d146 --- /dev/null +++ b/CryptLib/Encryption/PackingMode/ANSIx923.php @@ -0,0 +1,58 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Encryption\PackingMode; + +/** + * A packing mode implementation for ASNIx923 padding + * + * @category PHPCryptLib + * @package Encryption + * @subpackage PackingMode + * @author Anthony Ferrara + */ +class ANSIx923 implements \CryptLib\Encryption\PackingMode { + + /** + * Pad the string to the specified size + * + * @param string $string The string to pad + * @param int $blockSize The size to pad to + * + * @return string The padded string + */ + public function pad($string, $blockSize = 32) { + $pad = $blockSize - (strlen($string) % $blockSize); + return $string . str_repeat(chr(0), $pad - 1) . chr($pad); + } + + /** + * Strip the padding from the supplied string + * + * @param string $string The string to trim + * + * @return string The unpadded string + */ + public function strip($string) { + $end = ord(substr($string, -1)); + $len = strlen($string) - $end; + $tmp = str_repeat(chr(0), $end - 1) . chr($end); + if (substr($string, $len) == $tmp) { + return substr($string, 0, $len); + } + return false; + } + +} diff --git a/CryptLib/Encryption/PackingMode/ISO10126.php b/CryptLib/Encryption/PackingMode/ISO10126.php new file mode 100644 index 0000000..bd89015 --- /dev/null +++ b/CryptLib/Encryption/PackingMode/ISO10126.php @@ -0,0 +1,58 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Encryption\PackingMode; + +/** + * A Packing Mode implementation of the ISO 10126 standard + * + * @category PHPCryptLib + * @package Encryption + * @subpackage PackingMode + * @author Anthony Ferrara + */ +class ISO10126 implements \CryptLib\Encryption\PackingMode { + + /** + * Pad the string to the specified size + * + * @param string $string The string to pad + * @param int $blockSize The size to pad to + * + * @return string The padded string + */ + public function pad($string, $blockSize = 32) { + $pad = $blockSize - (strlen($string) % $blockSize); + $padstr = ''; + for ($i = 1; $i < $pad; $i++) { + $padstr .= chr(mt_rand(0, 255)); + } + return $string . $padstr . chr($pad); + } + + /** + * Strip the padding from the supplied string + * + * @param string $string The string to trim + * + * @return string The unpadded string + */ + public function strip($string) { + $end = ord(substr($string, -1)); + $len = strlen($string) - $end; + return substr($string, 0, $len); + } + +} diff --git a/CryptLib/Encryption/PackingMode/None.php b/CryptLib/Encryption/PackingMode/None.php new file mode 100644 index 0000000..95cb376 --- /dev/null +++ b/CryptLib/Encryption/PackingMode/None.php @@ -0,0 +1,55 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Encryption\PackingMode; + +/** + * A packing mode implementation for no padding + * + * This is provided for completeness only. Do not use this with actual ciphers + * + * @category PHPCryptLib + * @package Encryption + * @subpackage PackingMode + * @author Anthony Ferrara + */ +class None implements \CryptLib\Encryption\PackingMode { + + /** + * Pad the string to the specified size + * + * @param string $string The string to pad + * @param int $blockSize The size to pad to + * + * @return string The padded string + */ + public function pad($string, $blockSize = 32) { + return $string; + } + + /** + * Strip the padding from the supplied string + * + * @param string $string The string to trim + * + * @return string The unpadded string + */ + public function strip($string) { + return $string; + } + +} diff --git a/CryptLib/Encryption/PackingMode/PKCS7.php b/CryptLib/Encryption/PackingMode/PKCS7.php new file mode 100644 index 0000000..603e4fb --- /dev/null +++ b/CryptLib/Encryption/PackingMode/PKCS7.php @@ -0,0 +1,62 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Encryption\PackingMode; + +/** + * A packing mode implementation for PKCS7 padding + * + * PHP version 5.3 + * + * @see http://tools.ietf.org/html/rfc2315 + * @category PHPCryptLib + * @package Encryption + * @subpackage PackingMode + * @author Anthony Ferrara + */ +class PKCS7 implements \CryptLib\Encryption\PackingMode { + + /** + * Pad the string to the specified size + * + * @param string $string The string to pad + * @param int $blockSize The size to pad to + * + * @return string The padded string + */ + public function pad($string, $blockSize = 32) { + $pad = $blockSize - (strlen($string) % $blockSize); + return $string . str_repeat(chr($pad), $pad); + } + + /** + * Strip the padding from the supplied string + * + * @param string $string The string to trim + * + * @return string The unpadded string + */ + public function strip($string) { + $end = substr($string, -1); + $last = ord($end); + $len = strlen($string) - $last; + if (substr($string, $len) == str_repeat($end, $last)) { + return substr($string, 0, $len); + } + return false; + } + +} diff --git a/CryptLib/Encryption/PackingMode/Zeros.php b/CryptLib/Encryption/PackingMode/Zeros.php new file mode 100644 index 0000000..e5e6164 --- /dev/null +++ b/CryptLib/Encryption/PackingMode/Zeros.php @@ -0,0 +1,55 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Encryption\PackingMode; + +/** + * A packing mode implementation for null-byte padding + * + * @category PHPCryptLib + * @package Encryption + * @subpackage PackingMode + * @author Anthony Ferrara + */ +class Zeros implements \CryptLib\Encryption\PackingMode { + + /** + * Pad the string to the specified size + * + * @param string $string The string to pad + * @param int $blockSize The size to pad to + * + * @return string The padded string + */ + public function pad($string, $blockSize = 32) { + $pad = $blockSize - (strlen($string) % $blockSize); + if ($pad == $blockSize) { + return $string; + } + return $string . str_repeat(chr(0), $pad); + } + + /** + * Strip the padding from the supplied string + * + * @param string $string The string to trim + * + * @return string The unpadded string + */ + public function strip($string) { + return rtrim($string, chr(0)); + } + +} diff --git a/CryptLib/Hash/CRC32.php b/CryptLib/Hash/CRC32.php new file mode 100644 index 0000000..2fd8352 --- /dev/null +++ b/CryptLib/Hash/CRC32.php @@ -0,0 +1,111 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\Hash; + +/** + * An implementation of a streamable CRC32 + * + * PHP version 5.3 + * + * @category PHPCryptLib + * @package Hash + * @author Anthony Ferrara + */ +class CRC32 { + + /** + * @var int The initialal value for the CRC32 + */ + protected static $initValue = 0xFFFFFFFF; + + /** + * @var array The table of permutations for substituting CRC32 data with + */ + protected static $table = array( + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, + 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, + 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, + 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, + 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, + 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, + 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, + 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, + 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, + ); + + /** + * Compute a CRC32 value of a string + * + * @param string $data The data to calculate the crc32 for + * + * @return int The computed CRC32 value + */ + public static function calculate($data) { + return static::update(static::$initValue ^ 0xFFFFFFFF, $data); + } + + /** + * Updates an existing CRC32 value with a new byte (for a stream) + * + * @param int $crc32 The previous CRC32 value + * @param string $data The new byte to end (string length 1) + * + * @return int The updated CRC32 value + */ + public static function update($crc32, $data) { + $result = $crc32 ^ 0xFFFFFFFF; + $length = strlen($data); + for ($i = 0; $i < $length; $i++) { + $lowPart = $result & 0xFF; + $shifted = ($result >> 8) & 0xFFFFFF; + $result = static::$table[($lowPart ^ ord($data[$i])) & 0xFF] ^ $shifted; + } + return $result ^ 0xFFFFFFFF; + } + +} diff --git a/CryptLib/Hash/Hash.php b/CryptLib/Hash/Hash.php new file mode 100644 index 0000000..8c6f8a6 --- /dev/null +++ b/CryptLib/Hash/Hash.php @@ -0,0 +1,360 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\Hash; + +/** + * A hash utility data mapper class + * + * This class's purpose is to store information about hash algorithms that is + * otherwise unavailable during runtime. Some information is available (such + * as the output size), but is included anyway for performance and completeness + * reasons. + * + * PHP version 5.3 + * + * @category PHPCryptLib + * @package Hash + * @author Anthony Ferrara + */ +class Hash { + + /** + * This array contains information about each hash function available to PHP + * at the present time. Block sizes are not available from functions, so they + * must be hard coded. + * + * The "secure" indicates the strength of the hash and whether or not any known + * cryptographic attacks exist for the hash function. This will only apply when + * using the hash functions for situations that require cryptographic strength + * such as message signing. For other uses the insecure ones can have valid + * uses. + * + * @var array An array of information about each supported hash function + */ + protected static $hashInfo = array( + 'md2' => array( + 'HashSize' => 128, + 'BlockSize' => 128, + 'secure' => false, + ), + 'md4' => array( + 'HashSize' => 128, + 'BlockSize' => 512, + 'secure' => false, + ), + 'md5' => array( + 'HashSize' => 128, + 'BlockSize' => 512, + 'secure' => false, + ), + 'sha1' => array( + 'HashSize' => 160, + 'BlockSize' => 512, + 'secure' => false, + ), + 'sha224' => array( + 'HashSize' => 224, + 'BlockSize' => 512, + 'secure' => true, + ), + 'sha256' => array( + 'HashSize' => 256, + 'BlockSize' => 512, + 'secure' => true, + ), + 'sha384' => array( + 'HashSize' => 384, + 'BlockSize' => 1024, + 'secure' => true, + ), + 'sha512' => array( + 'HashSize' => 512, + 'BlockSize' => 1024, + 'secure' => true, + ), + 'ripemd128' => array( + 'HashSize' => 128, + 'BlockSize' => 512, + 'secure' => true, + ), + 'ripemd160' => array( + 'HashSize' => 160, + 'BlockSize' => 512, + 'secure' => true, + ), + 'ripemd256' => array( + 'HashSize' => 256, + 'BlockSize' => 512, + 'secure' => true, + ), + 'ripemd320' => array( + 'HashSize' => 320, + 'BlockSize' => 512, + 'secure' => true, + ), + 'whirlpool' => array( + 'HashSize' => 512, + 'BlockSize' => 512, + 'secure' => true, + ), + 'tiger128,3' => array( + 'HashSize' => 128, + 'BlockSize' => 512, + 'secure' => true, + ), + 'tiger160,3' => array( + 'HashSize' => 160, + 'BlockSize' => 512, + 'secure' => true, + ), + 'tiger192,3' => array( + 'HashSize' => 192, + 'BlockSize' => 512, + 'secure' => true, + ), + 'tiger128,4' => array( + 'HashSize' => 128, + 'BlockSize' => 512, + 'secure' => true, + ), + 'tiger160,4' => array( + 'HashSize' => 160, + 'BlockSize' => 512, + 'secure' => true, + ), + 'tiger192,4' => array( + 'HashSize' => 192, + 'BlockSize' => 512, + 'secure' => true, + ), + 'snefru' => array( + 'HashSize' => 256, + 'BlockSize' => 512, + 'secure' => false, + ), + 'snefru256' => array( + 'HashSize' => 256, + 'BlockSize' => 512, + 'secure' => false, + ), + 'gost' => array( + 'HashSize' => 256, + 'BlockSize' => 256, + 'secure' => false, + ), + 'adler32' => array( + 'HashSize' => 32, + 'BlockSize' => 16, + 'secure' => false, + ), + 'crc32' => array( + 'HashSize' => 32, + 'BlockSize' => 32, + 'secure' => false, + ), + 'crc32b' => array( + 'HashSize' => 32, + 'BlockSize' => 32, + 'secure' => false, + ), + 'salsa10' => array( + 'HashSize' => 512, + 'BlockSize' => 512, + 'secure' => true, + ), + 'salsa20' => array( + 'HashSize' => 512, + 'BlockSize' => 512, + 'secure' => true, + ), + 'haval128,3' => array( + 'HashSize' => 128, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval160,3' => array( + 'HashSize' => 160, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval192,3' => array( + 'HashSize' => 192, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval224,3' => array( + 'HashSize' => 224, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval256,3' => array( + 'HashSize' => 256, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval128,4' => array( + 'HashSize' => 128, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval160,4' => array( + 'HashSize' => 160, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval192,4' => array( + 'HashSize' => 192, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval224,4' => array( + 'HashSize' => 224, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval256,4' => array( + 'HashSize' => 256, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval128,5' => array( + 'HashSize' => 128, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval160,5' => array( + 'HashSize' => 160, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval192,5' => array( + 'HashSize' => 192, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval224,5' => array( + 'HashSize' => 224, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'haval256,5' => array( + 'HashSize' => 256, + 'BlockSize' => 1024, + 'secure' => false, + ), + 'joaat' => array( + 'HashSize' => 32, + 'BlockSize' => 64, + 'secure' => false, + ), + 'fnv132' => array( + 'HashSize' => 32, + 'BlockSize' => 32, + 'secure' => false, + ), + 'fnv164' => array( + 'HashSize' => 64, + 'BlockSize' => 64, + 'secure' => false, + ), + ); + + /** + * Get the block size of the specified function in bytes + * + * @param string $hash The hash function to look up + * + * @return int The number of bytes in the block function + */ + public static function getBlockSize($hash) { + return static::getBlockSizeInBits($hash) / 8; + } + + /** + * Get the block size of the specified function in bits + * + * @param string $hash The hash function to look up + * + * @return int The number of bits in the block function + */ + public static function getBlockSizeInBits($hash) { + if (isset(static::$hashInfo[$hash]['BlockSize'])) { + return static::$hashInfo[$hash]['BlockSize']; + } + return 0; + } + + /** + * Get the output size of the specified function in bytes + * + * @param string $hash The hash function to look up + * + * @return int The number of bytes outputted by the hash function + */ + public static function getHashSize($hash) { + return static::getHashSizeInBits($hash) / 8; + } + + /** + * Get the output size of the specified function in bits + * + * @param string $hash The hash function to look up + * + * @return int The number of bits outputted by the hash function + */ + public static function getHashSizeInBits($hash) { + if (isset(static::$hashInfo[$hash]['HashSize'])) { + return static::$hashInfo[$hash]['HashSize']; + } + return 0; + } + + /** + * Check to see if the hash function specified is available + * + * @param string $hash The hash function to look up + * + * @return boolean If the hash function is available in this version of PHP + */ + public static function isAvailable($hash) { + return in_array($hash, hash_algos()); + } + + /** + * Check to see if the specified hash function is secure enough for + * cryptographic uses + * + * The "secure" indicates the strength of the hash and whether or not any known + * cryptographic attacks exist for the hash function. This will only apply when + * using the hash functions for situations that require cryptographic strength + * such as message signing. For other uses the insecure ones can have valid + * uses. + * + * @param string $hash The hash function to look up + * + * @return bolean If the function is secure + */ + public static function isSecure($hash) { + if (isset(static::$hashInfo[$hash])) { + return static::$hashInfo[$hash]['secure']; + } + return false; + } + +} \ No newline at end of file diff --git a/CryptLib/Key/Derivation/AbstractDerivation.php b/CryptLib/Key/Derivation/AbstractDerivation.php new file mode 100644 index 0000000..775a082 --- /dev/null +++ b/CryptLib/Key/Derivation/AbstractDerivation.php @@ -0,0 +1,52 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation; + +/** + * An abstract implementation of some standard key derivation needs + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +abstract class AbstractDerivation { + + /** + * @var Hash A hashing algorithm to use for the derivation + */ + protected $hash = null; + + /** + * @var array An array of options for the key derivation function + */ + protected $options = array( + 'hash' => 'sha512', + ); + + /** + * Construct the derivation instance + * + * @param array $options An array of options to set for this instance + * + * @return void + */ + public function __construct(array $options = array()) { + $this->options = $options + $this->options; + $this->hash = $this->options['hash']; + } + +} diff --git a/CryptLib/Key/Derivation/KDF.php b/CryptLib/Key/Derivation/KDF.php new file mode 100644 index 0000000..5d7a26e --- /dev/null +++ b/CryptLib/Key/Derivation/KDF.php @@ -0,0 +1,40 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT Licenses + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation; + +/** + * The standard Key Derivation Function interface + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +interface KDF { + + /** + * Derive a key of the specified length based on the inputted secret + * + * @param string $secret The secret to base the key on + * @param int $length The length of the key to derive + * @param string $other Additional data to append to the key + * + * @return string The generated key + */ + public function derive($secret, $length, $other = ''); + +} diff --git a/CryptLib/Key/Derivation/KDF/KDF1.php b/CryptLib/Key/Derivation/KDF/KDF1.php new file mode 100644 index 0000000..0f45423 --- /dev/null +++ b/CryptLib/Key/Derivation/KDF/KDF1.php @@ -0,0 +1,54 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation\KDF; + +use CryptLib\Hash\Hash; + +/** + * An implementation of the RFC 10833-2 KDF1 Standard key derivation function + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +class KDF1 + extends \CryptLib\Key\Derivation\AbstractDerivation + implements \CryptLib\Key\Derivation\KDF +{ + + /** + * Derive a key of the specified length based on the inputted secret + * + * @param string $secret The secret to base the key on + * @param int $length The length of the key to derive + * @param string $other Additional data to append to the key + * + * @return string The generated key + */ + public function derive($secret, $length, $other = '') { + $size = Hash::getHashSize($this->hash); + $len = ceil($length / $size); + $res = ''; + for ($i = 0; $i < $len; $i++) { + $tmp = pack('N', $i); + $res .= hash($this->hash, $secret . $tmp . $other, true); + } + return substr($res, 0, $length); + } + +} + diff --git a/CryptLib/Key/Derivation/KDF/KDF2.php b/CryptLib/Key/Derivation/KDF/KDF2.php new file mode 100644 index 0000000..4bd3464 --- /dev/null +++ b/CryptLib/Key/Derivation/KDF/KDF2.php @@ -0,0 +1,54 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation\KDF; + +use CryptLib\Hash\Hash; + +/** + * An implementation of the RFC 10833-2 KDF2 Standard key derivation function + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +class KDF2 + extends \CryptLib\Key\Derivation\AbstractDerivation + implements \CryptLib\Key\Derivation\KDF +{ + + /** + * Derive a key of the specified length based on the inputted secret + * + * @param string $secret The secret to base the key on + * @param int $length The length of the key to derive + * @param string $other Additional data to append to the key + * + * @return string The generated key + */ + public function derive($secret, $length, $other = '') { + $size = Hash::getHashSize($this->hash); + $len = ceil($length / $size); + $res = ''; + for ($i = 1; $i <= $len; $i++) { + $tmp = pack('N', $i); + $res .= hash($this->hash, $secret . $tmp . $other, true); + } + return substr($res, 0, $length); + } + +} + diff --git a/CryptLib/Key/Derivation/KDF/KDF3.php b/CryptLib/Key/Derivation/KDF/KDF3.php new file mode 100644 index 0000000..90d3d6e --- /dev/null +++ b/CryptLib/Key/Derivation/KDF/KDF3.php @@ -0,0 +1,63 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation\KDF; + +use CryptLib\Hash\Hash; + +/** + * An implementation of the RFC 10833-2 KDF3 Standard key derivation function + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +class KDF3 + extends \CryptLib\Key\Derivation\AbstractDerivation + implements \CryptLib\Key\Derivation\KDF +{ + + /** + * @var array An array of options for the key derivation function + */ + protected $options = array( + 'hash' => 'sha512', + 'pAmt' => 4, + ); + + /** + * Derive a key of the specified length based on the inputted secret + * + * @param string $secret The secret to base the key on + * @param int $length The length of the key to derive + * @param string $other Additional data to append to the key + * + * @return string The generated key + */ + public function derive($secret, $length, $other = '') { + $size = Hash::getHashSize($this->hash); + $len = ceil($length / $size); + $res = ''; + $stub = str_repeat(chr(0), max($this->options['pAmt'], 0)); + for ($i = 0; $i < $len; $i++) { + $tmp = $stub . pack('N', $i); + $res .= hash($this->hash, $tmp . $secret . $other, true); + } + return substr($res, 0, $length); + } + +} + diff --git a/CryptLib/Key/Derivation/PBKDF.php b/CryptLib/Key/Derivation/PBKDF.php new file mode 100644 index 0000000..6275b2f --- /dev/null +++ b/CryptLib/Key/Derivation/PBKDF.php @@ -0,0 +1,57 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation; + +/** + * The core PBKDF interface (Password Based Key Derivation Function) + * + * This interface must be used to describe all derivation functions that take a + * password as input and produce a key or hash as output + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +interface PBKDF { + + /** + * Derive a key from the supplied arguments + * + * @param string $password The password to derive from + * @param string $salt The salt string to use + * @param int $iterations The number of iterations to use + * @param int $length The size of the string to generate + * + * @return string The derived key + */ + public function derive($passkey, $salt, $iterations, $klen); + + /** + * Get the signature for this implementation + * + * This should include all information needed to build the same isntance + * later. + * + * @return string The signature for this instance + */ + public function getSignature(); + +} diff --git a/CryptLib/Key/Derivation/PBKDF/BCrypt.php b/CryptLib/Key/Derivation/PBKDF/BCrypt.php new file mode 100644 index 0000000..94abd40 --- /dev/null +++ b/CryptLib/Key/Derivation/PBKDF/BCrypt.php @@ -0,0 +1,82 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation\PBKDF; + +/** + * An implementation of the crypt library's Blowfish hash method + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +class BCrypt + extends \CryptLib\Key\Derivation\AbstractDerivation + implements \CryptLib\Key\Derivation\PBKDF +{ + + /** + * Derive a key from the supplied arguments + * + * @param string $password The password to derive from + * @param string $salt The salt string to use + * @param int $iterations The number of iterations to use + * @param int $length The size of the string to generate + * + * @return string The derived key + */ + public function derive($password, $salt, $iterations, $length) { + $salt = $this->encode64($salt); + if (strlen($salt) > 22) { + $salt = substr($salt, 0, 22); + } elseif (strlen($salt) < 22) { + $salt = str_pad($salt, '0'); + } + $expense = ceil(log($iterations, 2)); + $expense = $expense < 4 ? 4 : $expense; + $expense = $expense > 31 ? 31 : $expense; + $salt = '$2a$'.str_pad($expense, 2, '0', STR_PAD_LEFT).'$'.$salt; + return crypt($password, $salt); + } + + /** + * Get the signature for this implementation + * + * This should include all information needed to build the same isntance + * later. + * + * @return string The signature for this instance + */ + public function getSignature() { + return 'bcrypt'; + } + + /** + * Encode a string for a blowfish salt + * + * @param string $string The string to encode + * + * @return The encoded string + */ + protected function encode64($string) { + return str_replace( + array('+', '='), + array('.', '/'), + base64_encode($string) + ); + } +} + diff --git a/CryptLib/Key/Derivation/PBKDF/PBKDF1.php b/CryptLib/Key/Derivation/PBKDF/PBKDF1.php new file mode 100644 index 0000000..9f8caa6 --- /dev/null +++ b/CryptLib/Key/Derivation/PBKDF/PBKDF1.php @@ -0,0 +1,71 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation\PBKDF; + +use CryptLib\Hash\Hash; + +/** + * An implementation of the RFC 2989 PBKDF1 Standard key derivation function + * + * @see http://www.ietf.org/rfc/rfc2898.txt + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +class PBKDF1 + extends \CryptLib\Key\Derivation\AbstractDerivation + implements \CryptLib\Key\Derivation\PBKDF +{ + + /** + * Derive a key from the supplied arguments + * + * @param string $password The password to derive from + * @param string $salt The salt string to use + * @param int $iterations The number of iterations to use + * @param int $length The size of the string to generate + * + * @return string The derived key + */ + public function derive($password, $salt, $iterations, $length) { + $size = Hash::getHashSize($this->hash); + if ($length > $size) { + $message = 'Length is too long for hash'; + throw new \InvalidArgumentException($message); + } + $tmp = hash($this->hash, $password . $salt, true); + for ($i = 2; $i <= $iterations; $i++) { + $tmp = hash($this->hash, $tmp, true); + } + return substr($tmp, 0, $length); + } + + /** + * Get the signature for this implementation + * + * This should include all information needed to build the same isntance + * later. + * + * @return string The signature for this instance + */ + public function getSignature() { + return 'pbkdf1-' . $this->hash; + } + +} + diff --git a/CryptLib/Key/Derivation/PBKDF/PBKDF2.php b/CryptLib/Key/Derivation/PBKDF/PBKDF2.php new file mode 100644 index 0000000..99a011e --- /dev/null +++ b/CryptLib/Key/Derivation/PBKDF/PBKDF2.php @@ -0,0 +1,79 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation\PBKDF; + +use CryptLib\Hash\Hash; + +/** + * An implementation of the RFC 2898 PBKDF2 Standard key derivation function + * + * @see http://www.ietf.org/rfc/rfc2898.txt + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +class PBKDF2 + extends \CryptLib\Key\Derivation\AbstractDerivation + implements \CryptLib\Key\Derivation\PBKDF +{ + + /** + * Derive a key from the supplied arguments + * + * @param string $password The password to derive from + * @param string $salt The salt string to use + * @param int $iterations The number of iterations to use + * @param int $length The size of the string to generate + * + * @return string The derived key + */ + public function derive($password, $salt, $iterations, $length) { + $size = Hash::getHashSize($this->hash); + $len = ceil($length / $size); + $result = ''; + for ($i = 1; $i <= $len; $i++) { + $tmp = hash_hmac( + $this->hash, + $salt . pack('N', $i), + $password, + true + ); + $res = $tmp; + for ($j = 1; $j < $iterations; $j++) { + $tmp = hash_hmac($this->hash, $tmp, $password, true); + $res ^= $tmp; + } + $result .= $res; + } + return substr($result, 0, $length); + } + + /** + * Get the signature for this implementation + * + * This should include all information needed to build the same isntance + * later. + * + * @return string The signature for this instance + */ + public function getSignature() { + return 'pbkdf2-' . $this->hash; + } + +} + diff --git a/CryptLib/Key/Derivation/PBKDF/SHA256.php b/CryptLib/Key/Derivation/PBKDF/SHA256.php new file mode 100644 index 0000000..a86f8dc --- /dev/null +++ b/CryptLib/Key/Derivation/PBKDF/SHA256.php @@ -0,0 +1,62 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation\PBKDF; + +/** + * An implementation of the crypt library's SHA512 hash method + * + * PHP version 5.3 + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +class SHA256 + extends \CryptLib\Key\Derivation\AbstractDerivation + implements \CryptLib\Key\Derivation\PBKDF +{ + + /** + * Derive a key from the supplied arguments + * + * @param string $password The password to derive from + * @param string $salt The salt string to use + * @param int $iterations The number of iterations to use + * @param int $length The size of the string to generate + * + * @return string The derived key + */ + public function derive($password, $salt, $iterations, $length) { + $salt = substr(str_pad($salt, 16, chr(0)), 0, 16); + $salt = '$5$rounds='.$iterations.'$'.$salt; + return crypt($password, $salt); + } + + /** + * Get the signature for this implementation + * + * This should include all information needed to build the same isntance + * later. + * + * @return string The signature for this instance + */ + public function getSignature() { + return 'sha256'; + } + +} + diff --git a/CryptLib/Key/Derivation/PBKDF/SHA512.php b/CryptLib/Key/Derivation/PBKDF/SHA512.php new file mode 100644 index 0000000..8d710e2 --- /dev/null +++ b/CryptLib/Key/Derivation/PBKDF/SHA512.php @@ -0,0 +1,60 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key\Derivation\PBKDF; + +/** + * An implementation of the crypt library's SHA512 hash method + * + * @category PHPCryptLib + * @package Key + * @subpackage Derivation + * @author Anthony Ferrara + */ +class SHA512 + extends \CryptLib\Key\Derivation\AbstractDerivation + implements \CryptLib\Key\Derivation\PBKDF +{ + + /** + * Derive a key from the supplied arguments + * + * @param string $password The password to derive from + * @param string $salt The salt string to use + * @param int $iterations The number of iterations to use + * @param int $length The size of the string to generate + * + * @return string The derived key + */ + public function derive($password, $salt, $iterations, $length) { + $salt = substr(str_pad($salt, 16, chr(0)), 0, 16); + $salt = '$6$rounds='.$iterations.'$'.$salt; + return crypt($password, $salt); + } + + /** + * Get the signature for this implementation + * + * This should include all information needed to build the same isntance + * later. + * + * @return string The signature for this instance + */ + public function getSignature() { + return 'sha512'; + } + +} + diff --git a/CryptLib/Key/Derivation/PBKDF/Schneier.php b/CryptLib/Key/Derivation/PBKDF/Schneier.php new file mode 100644 index 0000000..b3a7c74 --- /dev/null +++ b/CryptLib/Key/Derivation/PBKDF/Schneier.php @@ -0,0 +1,46 @@ +hash); + if ($length > $size) { + throw new \InvalidArgumentException('Length is too long for hash'); + } + $tmp = hash($this->hash, $password . $salt, true); + for ($i = 2; $i <= $iterations; $i++) { + $tmp = hash($this->hash, $tmp . $password . $salt, true); + } + return substr($tmp, 0, $length); + } + + /** + * Get the signature for this implementation + * + * This should include all information needed to build the same instance + * later. + * + * @return string The signature for this instance + */ + public function getSignature() { + return 'schneier-'.$this->hash; + } + +} diff --git a/CryptLib/Key/Factory.php b/CryptLib/Key/Factory.php new file mode 100644 index 0000000..e9ae3ea --- /dev/null +++ b/CryptLib/Key/Factory.php @@ -0,0 +1,110 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Key; + +/** + * The core Key Factory + * + * @category PHPCryptLib + * @package Key + * @author Anthony Ferrara + */ +class Factory extends \CryptLib\Core\AbstractFactory { + + /** + * @var array An array of KDF class implementations + */ + protected $kdf = array(); + + /** + * @var array An array of PBKDF class implementations + */ + protected $pbkdf = array(); + + /** + * @var array An array of symmetric key generator implementations + */ + protected $symmetricGenerators = array(); + + /** + * Construct the instance, loading the core implementations + * + * @return void + */ + public function __construct() { + $this->loadPBKDF(); + $this->loadKDF(); + //$this->loadSymmetricGenerators(); + } + + public function getKDF($name = 'kdf3', array $options = array()) { + if (isset($this->kdf[$name])) { + $class = $this->kdf[$name]; + return new $class($options); + } + throw new \InvalidArgumentException('Unsupported KDF'); + } + + public function getPBKDF($name = 'pbkdf2', array $options = array()) { + if (isset($this->pbkdf[$name])) { + $class = $this->pbkdf[$name]; + return new $class($options); + } + throw new \InvalidArgumentException('Unsupported PBKDF'); + } + + public function getPBKDFFromSignature($signature) { + list ($name, $hash) = explode('-', $signature, 2); + return $this->getPBKDF($name, array('hash' => $hash)); + } + + public function getSymmetricKeyGenerator() { + } + + public function registerKDF($name, $class) { + $this->registerType( + 'kdf', + __NAMESPACE__ . '\\Derivation\\KDF', + $name, + $class + ); + } + + public function registerPBKDF($name, $class) { + $this->registerType( + 'pbkdf', + __NAMESPACE__ . '\\Derivation\\PBKDF', + $name, + $class + ); + } + + protected function loadKDF() { + $this->loadFiles( + __DIR__ . '/Derivation/KDF', + __NAMESPACE__ . '\\Derivation\\KDF\\', + array($this, 'registerKDF') + ); + } + + protected function loadPBKDF() { + $this->loadFiles( + __DIR__ . '/Derivation/PBKDF', + __NAMESPACE__ . '\\Derivation\\PBKDF\\', + array($this, 'registerPBKDF') + ); + } + +} diff --git a/CryptLib/Key/Generator.php b/CryptLib/Key/Generator.php new file mode 100644 index 0000000..09c5ce0 --- /dev/null +++ b/CryptLib/Key/Generator.php @@ -0,0 +1,37 @@ +getKey(); + } + + public function getKey() { + return $this->key; + } + + public function getType() { + return self::SYMMETRIC; + } + + public function saveKey($filename) { + file_put_contents($filename, $this->getKey()); + } + +} diff --git a/CryptLib/Key/Symmetric/Generator/Internal.php b/CryptLib/Key/Symmetric/Generator/Internal.php new file mode 100644 index 0000000..3c374b4 --- /dev/null +++ b/CryptLib/Key/Symmetric/Generator/Internal.php @@ -0,0 +1,59 @@ + null, 'random' => null); + if (is_null($options['kdf'])) { + $factory = new KeyFactory(); + $options['kdf'] = $factory->getKdf('kdf3'); + } + $this->kdf = $options['kdf']; + if (is_null($options['random'])) { + $options['random'] = new RandomFactory(); + } + $this->random = $options['random']; + } + + public function __toString() { + } + + public function generate( + \CryptLib\Core\Strength $strength, + $size, + $passphrase = '' + ) { + $generator = $this->random->getGenerator($strength); + $seed = $generator->generate($size); + $key = $this->kdf->derive($seed, $size, $passphrase); + return new Raw(substr($key, 0, $size)); + } + + public function getType() { + return static::TYPE_SYMMETRIC; + } +} diff --git a/CryptLib/Key/Symmetric/Raw.php b/CryptLib/Key/Symmetric/Raw.php new file mode 100644 index 0000000..1d68a04 --- /dev/null +++ b/CryptLib/Key/Symmetric/Raw.php @@ -0,0 +1,22 @@ +key = $key; + } + +} + diff --git a/CryptLib/MAC/AbstractMAC.php b/CryptLib/MAC/AbstractMAC.php new file mode 100644 index 0000000..ee8189f --- /dev/null +++ b/CryptLib/MAC/AbstractMAC.php @@ -0,0 +1,41 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\MAC; + +/** + * An abstract class for MessageAuthenticationCode generation + * + * @category PHPCryptLib + * @package MAC + * @author Anthony Ferrara + */ +abstract class AbstractMAC implements MAC { + + /** + * @var array The stored options for this instance + */ + protected $options = array(); + + /** + * Build the instance of the MAC generator + * + * @param array $options The options for the instance + * + * @return void + */ + public function __construct(array $options = array()) { + $this->options = $options + $this->options; + } + +} diff --git a/CryptLib/MAC/Implementation/CMAC.php b/CryptLib/MAC/Implementation/CMAC.php new file mode 100644 index 0000000..3a36ff5 --- /dev/null +++ b/CryptLib/MAC/Implementation/CMAC.php @@ -0,0 +1,168 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\MAC\Implementation; + +use \CryptLib\Cipher\Factory; + +/** + * A Cipher based MAC generator (Based upon the CMAC specification) + * + * @see http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf + * @category PHPCryptLib + * @package MAC + * @subpackage Implementation + */ +class CMAC extends \CryptLib\MAC\AbstractMAC { + + protected $cipher = null; + + /** + * @var array The stored options for this instance + */ + protected $options = array( + 'cipher' => 'aes-128', + 'cipherFactory' => null, + ); + + /** + * Build the instance of the MAC generator + * + * @param array $options The options for the instance + * + * @return void + */ + public function __construct(array $options = array()) { + parent::__construct($options); + if (is_null($this->options['cipherFactory'])) { + $this->options['cipherFactory'] = new Factory; + } + $this->cipher = $this->options['cipherFactory']->getBlockCipher( + $this->options['cipher'] + ); + } + + /** + * Generate the MAC using the supplied data + * + * @param string $data The data to use to generate the MAC with + * @param string $key The key to generate the MAC + * @param int $size The size of the output to return + * + * @return string The generated MAC of the appropriate size + */ + public function generate($data, $key, $size = 0) { + $blockSize = $this->cipher->getBlockSize(); + if ($size == 0) { + $size = $blockSize; + } + if ($size > $blockSize) { + throw new \OutOfRangeException( + sprintf( + 'The size is too big for the cipher primitive [%d:%d]', + $size, + $blockSize + ) + ); + } + $this->cipher->setKey($key); + $keys = $this->generateKeys(); + $mBlocks = $this->splitDataIntoMBlocks($data, $keys); + $cBlock = str_repeat(chr(0), $blockSize); + foreach ($mBlocks as $key => $block) { + $cBlock = $this->cipher->encryptBlock($cBlock ^ $block); + } + return substr($cBlock, 0, $size); + } + + /** + * Generate a pair of keys by encrypting a block of all 0's, and then + * maniuplating the result + * + * @return array The generated keys + */ + protected function generateKeys() { + $keys = array(); + $blockSize = $this->cipher->getBlockSize(); + $rVal = $this->getRValue($blockSize); + $text = str_repeat(chr(0), $blockSize); + $lVal = $this->cipher->encryptBlock($text); + $keys[0] = $this->leftShift($lVal, 1); + if (ord(substr($lVal, 0, 1)) > 127) { + $keys[0] = $keys[0] ^ $rVal; + } + $keys[1] = $this->leftShift($keys[0], 1); + if (ord(substr($keys[0], 0, 1)) > 127) { + $keys[1] = $keys[1] ^ $rVal; + } + return $keys; + } + + /** + * Get an RValue based upon the block size + * + * @param int $size The size of the block in bytes + * + * @see http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf + * @return string A RValue of the appropriate block size + */ + protected function getRValue($size) { + switch ($size * 8) { + case 64: + return str_repeat(chr(0), 7) . chr(0x1B); + case 128: + return str_repeat(chr(0), 15) . chr(0x87); + default: + } + throw new \RuntimeException('Unsupported Block Size For The Cipher'); + } + + protected function leftShift($data, $bits) { + $mask = (0xff << (8 - $bits)) & 0xff; + $state = 0; + $result = ''; + $length = strlen($data); + for ($i = $length - 1; $i >= 0; $i--) { + $tmp = ord($data[$i]); + $result .= chr(($tmp << $bits) | $state); + $state = ($tmp & $mask) >> (8 - $bits); + } + return strrev($result); + } + + /** + * Split the data into appropriate block chunks, encoding with the kyes + * + * @param string $data The data to split + * @param array $keys The keys to use for encoding + * + * @return array The array of chunked and encoded data + */ + protected function splitDataIntoMBlocks($data, array $keys) { + $blockSize = $this->cipher->getBlockSize(); + $data = str_split($data, $blockSize); + $last = end($data); + if (strlen($last) != $blockSize) { + //Pad the last element + $last .= chr(0x80) . str_repeat(chr(0), $blockSize - 1 - strlen($last)); + $last = $last ^ $keys[1]; + } else { + $last = $last ^ $keys[0]; + } + $data[count($data) - 1] = $last; + return $data; + } + +} \ No newline at end of file diff --git a/CryptLib/MAC/Implementation/HMAC.php b/CryptLib/MAC/Implementation/HMAC.php new file mode 100644 index 0000000..475983c --- /dev/null +++ b/CryptLib/MAC/Implementation/HMAC.php @@ -0,0 +1,63 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\MAC\Implementation; + +use \CryptLib\Hash\Hash; + +/** + * A Hash-Base MAC generator + * + * @category PHPCryptLib + * @package MAC + * @subpackage Implementation + */ +class HMAC extends \CryptLib\MAC\AbstractMAC { + + /** + * @var array The stored options for this instance + */ + protected $options = array( + 'hash' => 'sha256', + ); + + /** + * Generate the MAC using the supplied data + * + * @param string $data The data to use to generate the MAC with + * @param string $key The key to generate the MAC + * @param int $size The size of the output to return + * + * @return string The generated MAC of the appropriate size + */ + public function generate($data, $key, $size = 0) { + $hash = $this->options['hash']; + $outputSize = Hash::getHashSize($hash); + if ($size == 0) { + $size = $outputSize; + } + if ($size > $outputSize) { + throw new \OutOfRangeException( + sprintf( + 'The size is too big for the hash primitive [%d:%d]', + $size, + $outputSize + ) + ); + } + $return = hash_hmac($hash, $data, $key, true); + return substr($return, 0, $size); + } + +} \ No newline at end of file diff --git a/CryptLib/MAC/MAC.php b/CryptLib/MAC/MAC.php new file mode 100644 index 0000000..eb6d857 --- /dev/null +++ b/CryptLib/MAC/MAC.php @@ -0,0 +1,45 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ +namespace CryptLib\MAC; + +/** + * The basic interface for MAC (Message Authentication Code) generation + * + * @category PHPCryptLib + * @package MAC + * @author Anthony Ferrara + */ +interface MAC { + + /** + * Build the instance of the MAC generator + * + * @param array $options The options for the instance + * + * @return void + */ + public function __construct(array $options = array()); + + /** + * Generate the MAC using the supplied data + * + * @param string $data The data to use to generate the MAC with + * @param string $key The key to generate the MAC + * @param int $size The size of the output to return + * + * @return string The generated MAC of the appropriate size + */ + public function generate($data, $key, $size = 0); + +} diff --git a/CryptLib/Password/Factory.php b/CryptLib/Password/Factory.php new file mode 100644 index 0000000..af3b5a0 --- /dev/null +++ b/CryptLib/Password/Factory.php @@ -0,0 +1,116 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password; + +use CryptLib\Password\Implementation\Blowfish; + +/** + * The Password Factory + * + * @category PHPCryptLib + * @package Password + * @author Anthony Ferrara + */ +class Factory extends \CryptLib\Core\AbstractFactory { + + /** + * @var array An array of implementation classes + */ + protected $implementations = array(); + + /** + * Build a new instance of the factory, loading core implementations + * + * @return void + */ + public function __construct() { + $this->loadImplementations(); + } + + /** + * Create a new password hash from the supplied password + * + * This defaults to using Blowfish if $prefix is not supplied + * + * @param string $password The password to hash + * @param string $prefix The prefix for the implementation + * + * @return string The hashed password + * @throws DomainException if the supplied prefix is not supported + */ + public function createHash($password, $prefix = '$2a$') { + if ($prefix === false) { + throw new \DomainException('Unsupported Prefix Supplied'); + } + foreach ($this->implementations as $impl) { + if ($impl::getPrefix() == $prefix) { + $instance = new $impl; + return $instance->create($password); + } + } + throw new \DomainException('Unsupported Prefix Supplied'); + } + + /** + * Verify a hash with a supplied password + * + * @param string $password The password to check against + * @param string $hash The hash to verify + * + * @return boolean True if valid, false if not + * @throws DomainException if the supplied prefix is not supported + */ + public function verifyHash($password, $hash) { + foreach ($this->implementations as $impl) { + if ($impl::detect($hash)) { + $instance = $impl::loadFromHash($hash); + return $instance->verify($password, $hash); + } + } + throw new \DomainException('Unsupported Password Hash Supplied'); + } + + /** + * Register a password implementation for this factory instance + * + * @param string $name The name of the stategy + * @param string $class The class name of the implementation + * + * @return Factory $this The current factory instance + */ + public function registerImplementation($name, $class) { + $this->registerType( + 'implementations', + __NAMESPACE__ . '\\Password', + $name, + $class + ); + return $this; + } + + /** + * Load all core password hashing implementations + * + * @return void + */ + protected function loadImplementations() { + $this->loadFiles( + __DIR__ . '/Implementation', + __NAMESPACE__ . '\\Implementation\\', + array($this, 'registerImplementation') + ); + } + +} \ No newline at end of file diff --git a/CryptLib/Password/Implementation/APR1.php b/CryptLib/Password/Implementation/APR1.php new file mode 100644 index 0000000..c0a4ac0 --- /dev/null +++ b/CryptLib/Password/Implementation/APR1.php @@ -0,0 +1,224 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password\Implementation; + +use CryptLib\Random\Factory as RandomFactory; + +/** + * The APR1 password hashing implementation + * + * Use this class to generate and validate APR1 password hashes. APR1 hashes + * are used primarrily by Apache for .htaccess password storage. + * + * @see http://httpd.apache.org/docs/2.2/misc/password_encryptions.html + * @category PHPCryptLib + * @package Password + * @subpackage Implementation + * @author Anthony Ferrara + */ +class APR1 implements \CryptLib\Password\Password { + + /** + * @var Generator The random generator to use for seeds + */ + protected $generator = null; + + /** + * @var Hash The hash function to use (MD5) + */ + protected $hash = null; + + /** + * @var int The number of iterations to perform (1000 for APR1) + */ + protected $iterations = 1000; + + /** + * Determine if the hash was made with this method + * + * @param string $hash The hashed data to check + * + * @return boolean Was the hash created by this method + */ + public static function detect($hash) { + return strncmp($hash, '$apr1$', 6) === 0; + } + + /** + * Return the prefix used by this hashing method + * + * @return string The prefix used + */ + public static function getPrefix() { + return '$apr1$'; + } + + /** + * Load an instance of the class based upon the supplied hash + * + * @param string $hash The hash to load from + * + * @return Password the created instance + * @throws InvalidArgumentException if the hash wasn't created here + */ + public static function loadFromHash($hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException('Hash Not Created Here'); + } + return new static; + } + + /** + * Build a new instance + * + * @param Generator $generator The random generator to use for seeds + * + * @return void + */ + public function __construct( + \CryptLib\Random\Generator $generator = null + ) { + if (is_null($generator)) { + $random = new RandomFactory(); + $generator = $random->getMediumStrengthGenerator(); + } + $this->generator = $generator; + } + + /** + * Create a password hash for a given plain text password + * + * @param string $password The password to hash + * + * @return string The formatted password hash + */ + public function create($password) { + $salt = $this->to64($this->generator->generateInt(0, PHP_INT_MAX), 8); + return $this->hash($password, $salt, $this->iterations); + } + + /** + * Verify a password hash against a given plain text password + * + * @param string $password The password to hash + * @param string $hash The supplied ahsh to validate + * + * @return boolean Does the password validate against the hash + */ + public function verify($password, $hash) { + $bits = explode('$', $hash); + if (!isset($bits[3]) || $bits[1] != 'apr1') { + return false; + } + $test = $this->hash($password, $bits[2], $this->iterations); + return $test == $hash; + } + + /** + * Perform the hashing of the password + * + * @param string $password The plain text password to hash + * @param string $salt The 8 byte salt to use + * @param int $iterations The number of iterations to use + * + * @return string The hashed password + */ + protected function hash($password, $salt, $iterations) { + $len = strlen($password); + $text = $password . '$apr1$' . $salt; + $bin = md5($password.$salt.$password, true); + for ($i = $len; $i > 0; $i -= 16) { + $text .= substr($bin, 0, min(16, $i)); + } + for ($i = $len; $i > 0; $i >>= 1) { + $text .= ($i & 1) ? chr(0) : $password[0]; + } + $bin = $this->iterate($text, $iterations, $salt, $password); + return $this->convertToHash($bin, $salt); + } + + protected function iterate($text, $iterations, $salt, $password) { + $bin = md5($text, true); + for ($i = 0; $i < $iterations; $i++) { + $new = ($i & 1) ? $password : $bin; + if ($i % 3) { + $new .= $salt; + } + if ($i % 7) { + $new .= $password; + } + $new .= ($i & 1) ? $bin : $password; + $bin = md5($new, true); + } + return $bin; + } + + protected function convertToHash($bin, $salt) { + $tmp = '$apr1$'.$salt.'$'; + $tmp .= $this->to64( + (ord($bin[0])<<16) | (ord($bin[6])<<8) | ord($bin[12]), + 4 + ); + $tmp .= $this->to64( + (ord($bin[1])<<16) | (ord($bin[7])<<8) | ord($bin[13]), + 4 + ); + $tmp .= $this->to64( + (ord($bin[2])<<16) | (ord($bin[8])<<8) | ord($bin[14]), + 4 + ); + $tmp .= $this->to64( + (ord($bin[3])<<16) | (ord($bin[9])<<8) | ord($bin[15]), + 4 + ); + $tmp .= $this->to64( + (ord($bin[4])<<16) | (ord($bin[10])<<8) | ord($bin[5]), + 4 + ); + $tmp .= $this->to64( + ord($bin[11]), + 2 + ); + return $tmp; + } + + /** + * Convert the input number to a base64 number of the specified size + * + * @param int $num The number to convert + * @param int $size The size of the result string + * + * @return string The converted representation + */ + protected function to64($num, $size) { + static $seed = ''; + if (empty($seed)) { + $seed = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + 'abcdefghijklmnopqrstuvwxyz'; + } + $result = ''; + while (--$size >= 0) { + $result .= $seed[$num & 0x3f]; + $num >>= 6; + } + return $result; + } + +} diff --git a/CryptLib/Password/Implementation/Blowfish.php b/CryptLib/Password/Implementation/Blowfish.php new file mode 100644 index 0000000..2205286 --- /dev/null +++ b/CryptLib/Password/Implementation/Blowfish.php @@ -0,0 +1,195 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password\Implementation; + +use CryptLib\Random\Factory as RandomFactory; + +/** + * The Blowfish password hashing implementation + * + * Use this class to generate and validate Blowfish password hashes. + * + * @category PHPCryptLib + * @package Password + * @subpackage Implementation + * @author Anthony Ferrara + */ +class Blowfish implements \CryptLib\Password\Password { + + /** + * @var Generator The random generator to use for seeds + */ + protected $generator = null; + + /** + * @var int The number of iterations to perform (base 2) + */ + protected $iterations = 10; + + /** + * Determine if the hash was made with this method + * + * @param string $hash The hashed data to check + * + * @return boolean Was the hash created by this method + */ + public static function detect($hash) { + static $regex = '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/'; + return 1 == preg_match($regex, $hash); + } + + /** + * Return the prefix used by this hashing method + * + * @return string The prefix used + */ + public static function getPrefix() { + if (version_compare(PHP_VERSION, '5.3.7') >= 0) { + return '$2y$'; + } else { + return '$2a$'; + } + } + + /** + * Load an instance of the class based upon the supplied hash + * + * @param string $hash The hash to load from + * + * @return Password the created instance + * @throws InvalidArgumentException if the hash wasn't created here + */ + public static function loadFromHash($hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException('Hash Not Created Here'); + } + list(, , $iterations) = explode('$', $hash, 4); + return new static((int) $iterations); + } + + /** + * Build a new instance + * + * @param int $iterations The number of times to iterate the hash + * @param Generator $generator The random generator to use for seeds + * + * @return void + */ + public function __construct( + $iterations = 8, + \CryptLib\Random\Generator $generator = null + ) { + if ($iterations > 31 || $iterations < 4) { + throw new \InvalidArgumentException('Invalid Iteration Count Supplied'); + } + $this->iterations = $iterations; + if (is_null($generator)) { + $random = new RandomFactory(); + $generator = $random->getMediumStrengthGenerator(); + } + $this->generator = $generator; + } + + /** + * Create a password hash for a given plain text password + * + * @param string $password The password to hash + * + * @return string The formatted password hash + */ + public function create($password) { + /** + * Check for security flaw in the bcrypt implementation used by crypt() + * @see http://php.net/security/crypt_blowfish.php + */ + $match = preg_match('/[\x80-\xFF]/', $password); + if (version_compare(PHP_VERSION, '5.3.7', '<') && $match) { + throw new \RuntimeException( + 'The bcrypt implementation used by PHP contains a security flaw ' . + 'for password with 8-bit character. We suggest to upgrade to ' . + 'PHP 5.3.7+ or use passwords with only 7-bit characters' + ); + } + $salt = $this->to64($this->generator->generate(16)); + $prefix = static::getPrefix(); + $prefix .= str_pad($this->iterations, 2, '0', STR_PAD_LEFT); + $saltstring = $prefix . '$' . $salt; + $result = crypt($password, $saltstring); + if ($result[0] == '*') { + //@codeCoverageIgnoreStart + throw new \RuntimeException('Password Could Not Be Created'); + //@codeCoverageIgnoreEnd + } + return $result; + } + + /** + * Verify a password hash against a given plain text password + * + * @param string $password The password to hash + * @param string $hash The supplied ahsh to validate + * + * @return boolean Does the password validate against the hash + */ + public function verify($password, $hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException( + 'The hash was not created here, we cannot verify it' + ); + } + $test = crypt($password, $hash); + return $test == $hash; + } + + /** + * Convert the input number to a base64 number of the specified size + * + * @param int $input The number to convert + * + * @return string The converted representation + */ + protected function to64($input) { + static $itoa = null; + if (empty($itoa)) { + $itoa = './ABCDEFGHIJKLMNOPQRSTUVWXYZ' + . 'abcdefghijklmnopqrstuvwxyz0123456789'; + } + $output = ''; + $size = strlen($input); + $ictr = 0; + do { + $cval1 = ord($input[$ictr++]); + $output .= $itoa[$cval1 >> 2]; + $cval1 = ($cval1 & 0x03) << 4; + if ($ictr >= $size) { + $output .= $itoa[$cval1]; + break; + } + $cval2 = ord($input[$ictr++]); + $cval1 |= $cval2 >> 4; + $output .= $itoa[$cval1]; + $cval1 = ($cval2 & 0x0f) << 2; + $cval2 = ord($input[$ictr++]); + $cval1 |= $cval2 >> 6; + $output .= $itoa[$cval1]; + $output .= $itoa[$cval2 & 0x3f]; + } while (true); + return $output; + } + +} \ No newline at end of file diff --git a/CryptLib/Password/Implementation/Drupal.php b/CryptLib/Password/Implementation/Drupal.php new file mode 100644 index 0000000..98ef34c --- /dev/null +++ b/CryptLib/Password/Implementation/Drupal.php @@ -0,0 +1,58 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password\Implementation; + +use CryptLib\Random\Factory as RandomFactory; + +/** + * The PHPASS password hashing implementation + * + * Use this class to generate and validate PHPASS password hashes. + * + * @see http://www.openwall.com/phpass/ + * @category PHPCryptLib + * @package Password + * @subpackage Implementation + * @author Anthony Ferrara + */ +class Drupal extends PHPASS { + + /** + * @var string The prefix for the generated hash + */ + protected static $prefix = '$S$'; + + /** + * @var string The hash function to use for this instance + */ + protected $hashFunction = 'sha512'; + + /** + * Determine if the hash was made with this method + * + * @param string $hash The hashed data to check + * + * @return boolean Was the hash created by this method + */ + public static function detect($hash) { + $prefix = preg_quote(static::$prefix, '/'); + return 1 == preg_match('/^'.$prefix.'[a-zA-Z0-9.\/]{95}$/', $hash); + } + +} \ No newline at end of file diff --git a/CryptLib/Password/Implementation/Hash.php b/CryptLib/Password/Implementation/Hash.php new file mode 100644 index 0000000..b85b00c --- /dev/null +++ b/CryptLib/Password/Implementation/Hash.php @@ -0,0 +1,145 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password\Implementation; + +use CryptLib\Random\Factory as RandomFactory; + +/** + * The basic Hash implementation. + * + * It's worth noting, since there's no prefix, you cannot create a hash using + * the factory method. + * + * @category PHPCryptLib + * @package Password + * @subpackage Implementation + * @author Anthony Ferrara + */ +class Hash implements \CryptLib\Password\Password { + + /** + * @var Generator The random generator to use for seeds + */ + protected $generator = null; + + /** + * @var Hash The hash function to use (MD5) + */ + protected $hash = null; + + /** + * Determine if the hash was made with this method + * + * @param string $hash The hashed data to check + * + * @return boolean Was the hash created by this method + */ + public static function detect($hash) { + $res = preg_match('/^[a-fA-F0-9]+$/', $hash); + $res &= (int) in_array(strlen($hash), array(32, 40, 64, 128)); + return (boolean) $res; + } + + /** + * Return the prefix used by this hashing method + * + * @return string The prefix used + */ + public static function getPrefix() { + return false; + } + + /** + * Load an instance of the class based upon the supplied hash + * + * @param string $hash The hash to load from + * + * @return Password the created instance + * @throws InvalidArgumentException if the hash wasn't created here + */ + public static function loadFromHash($hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException('Hash Not Created Here'); + } + $hashMethod = ''; + switch (strlen($hash)) { + case 32: + $hashMethod = 'md5'; + break; + case 40: + $hashMethod = 'sha1'; + break; + case 64: + $hashMethod = 'sha256'; + break; + case 128: + $hashMethod = 'sha512'; + break; + } + return new static($hashMethod); + } + + /** + * Build a new instance + * + * @param string $hashMethod The hash function to use for hashing + * @param Generator $generator The random generator to use for seeds + * @param Factory $factory The hash factory to use for this instance + * + * @return void + */ + public function __construct( + $hashMethod, + \CryptLib\Random\Generator $generator = null + ) { + $this->hash = $hashMethod; + if (is_null($generator)) { + $random = new RandomFactory(); + $generator = $random->getMediumStrengthGenerator(); + } + $this->generator = $generator; + } + + /** + * Create a password hash for a given plain text password + * + * @param string $password The password to hash + * + * @return string The formatted password hash + */ + public function create($password) { + throw new \BadMethodCallException( + 'Unsalted Passwords are only implemented for verification' + ); + } + + /** + * Verify a password hash against a given plain text password + * + * @param string $password The password to hash + * @param string $hash The supplied ahsh to validate + * + * @return boolean Does the password validate against the hash + */ + public function verify($password, $hash) { + $test = hash($this->hash, $password); + return $test == $hash; + } + +} diff --git a/CryptLib/Password/Implementation/Joomla.php b/CryptLib/Password/Implementation/Joomla.php new file mode 100644 index 0000000..62f7ad3 --- /dev/null +++ b/CryptLib/Password/Implementation/Joomla.php @@ -0,0 +1,127 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password\Implementation; + +use CryptLib\Random\Factory as RandomFactory; + +/** + * The Joomla based hash implementation based off of the md5-hex hash method + * + * It's worth noting, since there's no prefix, you cannot create a hash using + * the factory method. + * + * @category PHPCryptLib + * @package Password + * @subpackage Implementation + * @author Anthony Ferrara + */ +class Joomla implements \CryptLib\Password\Password { + + /** + * @var Generator The random generator to use for seeds + */ + protected $generator = null; + + /** + * Determine if the hash was made with this method + * + * @param string $hash The hashed data to check + * + * @return boolean Was the hash created by this method + */ + public static function detect($hash) { + return (boolean) preg_match('/^[a-fA-F0-9]{32}:[a-zA-z0-9]{32}$/', $hash); + } + + /** + * Return the prefix used by this hashing method + * + * @return string The prefix used + */ + public static function getPrefix() { + return false; + } + + /** + * Load an instance of the class based upon the supplied hash + * + * @param string $hash The hash to load from + * + * @return Password the created instance + * @throws InvalidArgumentException if the hash wasn't created here + */ + public static function loadFromHash($hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException('Hash Not Created Here'); + } + return new static(); + } + + /** + * Build a new instance + * + * @param Generator $generator The random generator to use for seeds + * @param Factory $factory The hash factory to use for this instance + * + * @return void + */ + public function __construct( + \CryptLib\Random\Generator $generator = null + ) { + if (is_null($generator)) { + $random = new RandomFactory(); + $generator = $random->getMediumStrengthGenerator(); + } + $this->generator = $generator; + } + + /** + * Create a password hash for a given plain text password + * + * @param string $password The password to hash + * + * @return string The formatted password hash + */ + public function create($password) { + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + $salt = $this->generator->generateString(32, $chars); + $hash = md5($password . $salt); + return $hash . ':' . $salt; + } + + /** + * Verify a password hash against a given plain text password + * + * @param string $password The password to hash + * @param string $hash The supplied ahsh to validate + * + * @return boolean Does the password validate against the hash + */ + public function verify($password, $hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException( + 'The hash was not created here, we cannot verify it' + ); + } + list ($hash, $salt) = explode(':', $hash, 2); + $test = md5($password . $salt); + return $test == $hash; + } + +} diff --git a/CryptLib/Password/Implementation/PBKDF.php b/CryptLib/Password/Implementation/PBKDF.php new file mode 100644 index 0000000..577053c --- /dev/null +++ b/CryptLib/Password/Implementation/PBKDF.php @@ -0,0 +1,188 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password\Implementation; + +use CryptLib\Key\Factory as KeyFactory; +use CryptLib\Random\Factory as RandomFactory; +use CryptLib\Key\Derivation\PBKDF\PBKDF2 as PBKDF2; + +/** + * The PBKDF based password hashing implementation + * + * Use this class to generate and validate PBKDF hashed passwords. + * + * PHP version 5.3 + * + * @see http://httpd.apache.org/docs/2.2/misc/password_encryptions.html + * @category PHPCryptLib + * @package Password + * @subpackage Implementation + * @author Anthony Ferrara + */ +class PBKDF implements \CryptLib\Password\Password { + + /** + * @var PBKDF The PBKDF derivation implementation to use for this instance + */ + protected $derivation = null; + + /** + * @var Generator The Random Number Generator to use for making salts + */ + protected $generator = null; + + /** + * @var int The number of iterations to perform on the password + */ + protected $iterations = 5000; + + /** + * @var int The length in bytes of the generated password hash + */ + protected $size = 40; + + /** + * Determine if the hash was made with this method + * + * @param string $hash The hashed data to check + * + * @return boolean Was the hash created by this method + */ + public static function detect($hash) { + return strncmp($hash, '$pbkdf$', 7) === 0; + } + + /** + * Return the prefix used by this hashing method + * + * @return string The prefix used + */ + public static function getPrefix() { + return '$pbkdf$'; + } + + /** + * Load an instance of the class based upon the supplied hash + * + * @param string $hash The hash to load from + * + * @return Password the created instance + * @throws InvalidArgumentException if the hash wasn't created here + */ + public static function loadFromHash($hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException('Hash Not Created Here'); + } + $parts = explode('$', $hash); + if (count($parts) != 7) { + throw new \InvalidArgumentException('Hash Not Created Here'); + } + $signature = $parts[2]; + $factory = new KeyFactory(); + $hash = $factory->getPBKDFFromSignature($signature); + $iterations = $parts[3]; + $size = $parts[4]; + return new static($hash, $size, $iterations); + } + + /** + * Build a new instance of the PBKDF password class + * + * @param PBKDF $derivation The derivation class to use + * @param int $size The size of hash to generate + * @param int $iterations The number of iterations to perform + * @param Generator $generator The Random Generator to use + * + * @return void; + */ + public function __construct( + \CryptLib\Key\Derivation\PBKDF $derivation = null, + $size = 40, + $iterations = 5000, + \CryptLib\Random\Generator $generator = null + ) { + if (is_null($derivation)) { + $derivation = new PBKDF2(); + } + $this->derivation = $derivation; + $this->size = $size < 40 ? 40 : (int) $size; + $this->iterations = $iterations > 0 ? (int) $iterations : 1; + if (is_null($generator)) { + $factory = new RandomFactory; + $generator = $factory->getMediumStrengthGenerator(); + } + $this->generator = $generator; + } + + /** + * Create a password hash for a given plain text password + * + * @param string $password The password to hash + * + * @return string The formatted password hash + */ + public function create($password) { + $size = $this->size - 8; // remove size of stored bits + $saltSize = floor($size / 5); //Use 20% of the size for the salt + $hashSize = $size - $saltSize; + $salt = $this->generator->generate($saltSize); + return $this->hash($password, $salt, $this->iterations, $hashSize); + } + + /** + * Verify a password hash against a given plain text password + * + * @param string $password The password to hash + * @param string $hash The supplied ahsh to validate + * + * @return boolean Does the password validate against the hash + */ + public function verify($password, $hash) { + if (strlen($hash) <= 16 || strpos($hash, '$') === false) { + return false; + } + $parts = explode('$', $hash); + if (count($parts) != 7) { + return false; + } elseif ($parts[2] != $this->derivation->getSignature()) { + return false; + } + $iterations = $parts[3]; + $size = $parts[4]; + $salt = base64_decode($parts[5]); + return $this->hash($password, $salt, $iterations, $size) == $hash; + } + + /** + * Perform the hashing of the password + * + * @param string $password The plain text password to hash + * @param string $salt The 8 byte salt to use + * @param int $iterations The number of iterations to use + * + * @return string The hashed password + */ + protected function hash($password, $salt, $iterations, $size) { + $bit = $this->derivation->derive($password, $salt, $iterations, $size); + $sig = $this->derivation->getSignature(); + $sig = '$pbkdf$' . $sig . '$' . $iterations . '$' . $size; + return $sig . '$' . base64_encode($salt) . '$' . base64_encode($bit); + } + +} diff --git a/CryptLib/Password/Implementation/PHPASS.php b/CryptLib/Password/Implementation/PHPASS.php new file mode 100644 index 0000000..4522e87 --- /dev/null +++ b/CryptLib/Password/Implementation/PHPASS.php @@ -0,0 +1,246 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password\Implementation; + +use CryptLib\Random\Factory as RandomFactory; + +/** + * The PHPASS password hashing implementation + * + * Use this class to generate and validate PHPASS password hashes. + * + * @see http://www.openwall.com/phpass/ + * @category PHPCryptLib + * @package Password + * @subpackage Implementation + * @author Anthony Ferrara + */ +class PHPASS implements \CryptLib\Password\Password { + + /** + * @var string The ITOA string to be used for base64 conversion + */ + protected static $itoa = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ + abcdefghijklmnopqrstuvwxyz'; + + /** + * @var Generator The random generator to use for seeds + */ + protected $generator = null; + + /** + * This is the hash function to use. To be overriden by child classes + * + * @var string The hash function to use for this instance + */ + protected $hashFunction = 'md5'; + + /** + * @var int The number of iterations to perform (base 2) + */ + protected $iterations = 10; + + /** + * @var string The prefix for the generated hash + */ + protected static $prefix = '$P$'; + + /** + * Determine if the hash was made with this method + * + * @param string $hash The hashed data to check + * + * @return boolean Was the hash created by this method + */ + public static function detect($hash) { + $prefix = preg_quote(static::$prefix, '/'); + return 1 == preg_match('/^'.$prefix.'[a-zA-Z0-9.\/]{31}$/', $hash); + } + + /** + * Return the prefix used by this hashing method + * + * @return string The prefix used + */ + public static function getPrefix() { + return static::$prefix; + } + + /** + * Initialize the password hasher by replacing away spaces in the itoa var + * + * @return void + */ + public static function init() { + static::$itoa = preg_replace('/\s/', '', static::$itoa); + } + + /** + * Load an instance of the class based upon the supplied hash + * + * @param string $hash The hash to load from + * + * @return Password the created instance + * @throws InvalidArgumentException if the hash wasn't created here + */ + public static function loadFromHash($hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException('Hash Not Created Here'); + } + $iterations = static::decodeIterations($hash[3]); + return new static($iterations); + } + + /** + * Decode an ITOA encoded iteration count + * + * @param string $byte The character to decode + * + * @return int The decoded iteration count (base2) + */ + protected static function decodeIterations($byte) { + return strpos(static::$itoa, $byte); + } + + /** + * Encode a base2 iteration count to a base64 character + * + * @param int $number + * + * @return string The encoded character + */ + protected static function encodeIterations($number) { + return static::$itoa[$number]; + } + + /** + * Build a new instance + * + * @param int $iterations The number of times to iterate the hash + * @param Generator $generator The random generator to use for seeds + * @param Factory $factory The hash factory to use for this instance + * + * @return void + */ + public function __construct( + $iterations = 8, + \CryptLib\Random\Generator $generator = null + ) { + if ($iterations > 30 || $iterations < 7) { + throw new \InvalidArgumentException('Invalid Iteration Count Supplied'); + } + $this->iterations = $iterations; + if (is_null($generator)) { + $random = new RandomFactory(); + $generator = $random->getMediumStrengthGenerator(); + } + $this->generator = $generator; + } + + /** + * Create a password hash for a given plain text password + * + * @param string $password The password to hash + * + * @return string The formatted password hash + */ + public function create($password) { + $salt = $this->to64($this->generator->generate(6)); + $prefix = static::encodeIterations($this->iterations) . $salt; + return static::$prefix . $prefix . $this->hash($password, $salt); + } + + /** + * Verify a password hash against a given plain text password + * + * @param string $password The password to hash + * @param string $hash The supplied ahsh to validate + * + * @return boolean Does the password validate against the hash + */ + public function verify($password, $hash) { + if (!static::detect($hash)) { + throw new \InvalidArgumentException( + 'The hash was not created here, we cannot verify it' + ); + } + $iterations = static::decodeIterations($hash[3]); + if ($iterations != $this->iterations) { + throw new \InvalidArgumentException( + 'Iteration Count Mismatch, Bailing' + ); + } + $salt = substr($hash, 4, 8); + $hash = substr($hash, 12); + $test = $this->hash($password, $salt); + return $test == $hash; + } + + /** + * Execute the hash function with proper iterations + * + * @param string $password The password to hash + * @param string $salt The salt to use to hash + * + * @return string The base64 encoded generated hash + */ + protected function hash($password, $salt) { + $count = 1 << $this->iterations; + $hash = hash($this->hashFunction, $salt . $password, true); + do { + $hash = hash($this->hashFunction, $hash . $password, true); + } while (--$count); + return $this->to64($hash); + } + + /** + * Convert the input number to a base64 number of the specified size + * + * @param int $input The number to convert + * + * @return string The converted representation + */ + protected function to64($input) { + $output = ''; + $count = strlen($input); + $ictr = 0; + do { + $value = ord($input[$ictr++]); + $output .= static::$itoa[$value & 0x3f]; + if ($ictr < $count) { + $value |= ord($input[$ictr]) << 8; + } + $output .= static::$itoa[($value >> 6) & 0x3f]; + if ($ictr++ >= $count) { + break; + } + if ($ictr < $count) { + $value |= ord($input[$ictr]) << 16; + } + $output .= static::$itoa[($value >> 12) & 0x3f]; + if ($ictr++ < $count) { + $output .= static::$itoa[($value >> 18) & 0x3f]; + } + } while ($ictr < $count); + return $output; + } + +} + +PHPASS::init(); \ No newline at end of file diff --git a/CryptLib/Password/Implementation/PHPBB.php b/CryptLib/Password/Implementation/PHPBB.php new file mode 100644 index 0000000..ce108f6 --- /dev/null +++ b/CryptLib/Password/Implementation/PHPBB.php @@ -0,0 +1,41 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password\Implementation; + +use CryptLib\Random\Factory as RandomFactory; + +/** + * The PHPBB password hashing implementation + * + * Use this class to generate and validate PHPBB password hashes. + * + * @see http://www.openwall.com/phpass/ + * @category PHPCryptLib + * @package Password + * @subpackage Implementation + * @author Anthony Ferrara + */ +class PHPBB extends PHPASS { + + /** + * @var string The prefix for the generated hash + */ + protected static $prefix = '$H$'; + +} \ No newline at end of file diff --git a/CryptLib/Password/Password.php b/CryptLib/Password/Password.php new file mode 100644 index 0000000..e2487ce --- /dev/null +++ b/CryptLib/Password/Password.php @@ -0,0 +1,75 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Password; + +/** + * The core password key interface + * + * All pasword implementations must implement this interface + * + * @category PHPCryptLib + * @package Password + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +interface Password { + + /** + * Determine if the hash was made with this method + * + * @param string $hash The hashed data to check + * + * @return boolean Was the hash created by this method + */ + public static function detect($hash); + + /** + * Return the prefix used by this hashing method + * + * @return string The prefix used + */ + public static function getPrefix(); + + /** + * Load an instance of the class based upon the supplied hash + * + * @param string $hash The hash to load from + * + * @return Password the created instance + */ + public static function loadFromHash($hash); + + /** + * Create a password hash for a given plain text password + * + * @param string $password The password to hash + * + * @return string The formatted password hash + */ + public function create($password); + + /** + * Verify a password hash against a given plain text password + * + * @param string $password The password to hash + * @param string $hash The supplied ahsh to validate + * + * @return boolean Does the password validate against the hash + */ + public function verify($password, $hash); + +} diff --git a/CryptLib/Random/AbstractMixer.php b/CryptLib/Random/AbstractMixer.php new file mode 100644 index 0000000..6864058 --- /dev/null +++ b/CryptLib/Random/AbstractMixer.php @@ -0,0 +1,115 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random; + +/** + * An abstract mixer to implement a common mixing strategy + * + * @see http://tools.ietf.org/html/rfc4086#section-5.2 + * @category PHPCryptLib + * @package Random + * @author Anthony Ferrara + */ +abstract class AbstractMixer implements \CryptLib\Random\Mixer { + + /** + * Get the block size (the size of the individual blocks used for the mixing) + * + * @return int The block size + */ + abstract protected function getPartSize(); + + /** + * Mix 2 parts together using one method + * + * @param string $part1 The first part to mix + * @param string $part2 The second part to mix + * + * @return string The mixed data + */ + abstract protected function mixParts1($part1, $part2); + + /** + * Mix 2 parts together using another different method + * + * @param string $part1 The first part to mix + * @param string $part2 The second part to mix + * + * @return string The mixed data + */ + abstract protected function mixParts2($part1, $part2); + + /** + * Mix the provided array of strings into a single output of the same size + * + * All elements of the array should be the same size. + * + * @param array $parts The parts to be mixed + * + * @return string The mixed result + */ + public function mix(array $parts) { + if (empty($parts)) { + return ''; + } + $len = strlen($parts[0]); + $parts = $this->normalizeParts($parts); + $stringSize = count($parts[0]); + $partsSize = count($parts); + $result = ''; + $offset = 0; + for ($i = 0; $i < $stringSize; $i++) { + $stub = $parts[$offset][$i]; + for ($j = 1; $j < $partsSize; $j++) { + $newKey = $parts[($j + $offset) % $partsSize][$i]; + //Alternately mix the output for each source + if ($j % 2 == 1) { + $stub ^= $this->mixParts1($stub, $newKey); + } else { + $stub ^= $this->mixParts2($stub, $newKey); + } + } + $result .= $stub; + $offset = ($offset + 1) % $partsSize; + } + return substr($result, 0, $len); + } + + /** + * Normalize the part array and split it block part size. + * + * This will make all parts the same length and a multiple + * of the part size + * + * @param array $parts The parts to normalize + * + * @return array The normalized and split parts + */ + protected function normalizeParts(array $parts) { + $blockSize = $this->getPartSize(); + $callback = function($value) { + return strlen($value); + }; + $maxSize = max(array_map($callback, $parts)); + if ($maxSize % $blockSize != 0) { + $maxSize += $blockSize - ($maxSize % $blockSize); + } + foreach ($parts as &$part) { + $part = str_pad($part, $maxSize, chr(0)); + $part = str_split($part, $blockSize); + } + return $parts; + } +} diff --git a/CryptLib/Random/Factory.php b/CryptLib/Random/Factory.php new file mode 100644 index 0000000..468624a --- /dev/null +++ b/CryptLib/Random/Factory.php @@ -0,0 +1,221 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random; + +use CryptLib\Core\Strength; + +/** + * The Random Factory + * + * Use this factory to instantiate random number generators, sources and mixers. + * + * @category PHPCryptLib + * @package Random + * @author Anthony Ferrara + */ +class Factory extends \CryptLib\Core\AbstractFactory { + + /** + * @var array A list of available random number mixing strategies + */ + protected $mixers = array(); + + /** + * @var array A list of available random number sources + */ + protected $sources = array(); + + /** + * Build a new instance of the factory, loading core mixers and sources + * + * @return void + */ + public function __construct() { + $this->loadMixers(); + $this->loadSources(); + } + + /** + * Get a generator for the requested strength + * + * @param Strength $strength The requested strength of the random number + * + * @return Generator The instantiated generator + * @throws RuntimeException If an appropriate mixing strategy isn't found + */ + public function getGenerator(\CryptLib\Core\Strength $strength) { + $sources = $this->getSources(); + $newSources = array(); + foreach ($sources as $source) { + if ($strength->compare($source::getStrength()) <= 0) { + $newSources[] = new $source; + } + } + $mixer = $this->findMixer($strength); + return new Generator($newSources, $mixer); + } + + /** + * Get a high strength random number generator + * + * High Strength keys should ONLY be used for generating extremely strong + * cryptographic keys. Generating them is very resource intensive and may + * take several minutes or more depending on the requested size. + * + * @return Generator The instantiated generator + */ + public function getHighStrengthGenerator() { + return $this->getGenerator(new Strength(Strength::HIGH)); + } + + /** + * Get a low strength random number generator + * + * Low Strength should be used anywhere that random strings are needed in a + * non-cryptographical setting. They are not strong enough to be used as + * keys or salts. They are however useful for one-time use tokens. + * + * @return Generator The instantiated generator + */ + public function getLowStrengthGenerator() { + return $this->getGenerator(new Strength(Strength::LOW)); + } + + /** + * Get a medium strength random number generator + * + * Medium Strength should be used for most needs of a cryptographic nature. + * They are strong enough to be used as keys and salts. However, they do + * take some time and resources to generate, so they should not be over-used + * + * @return Generator The instantiated generator + */ + public function getMediumStrengthGenerator() { + return $this->getGenerator(new Strength(Strength::MEDIUM)); + } + + /** + * Get all loaded mixing strategies + * + * @return array An array of mixers + */ + public function getMixers() { + return $this->mixers; + } + + /** + * Get all loaded random number sources + * + * @return array An array of sources + */ + public function getSources() { + return $this->sources; + } + + /** + * Register a mixing strategy for this factory instance + * + * @param string $name The name of the stategy + * @param string $class The class name of the implementation + * + * @return Factory $this The current factory instance + */ + public function registerMixer($name, $class) { + $this->registerType( + 'mixers', + __NAMESPACE__ . '\\Mixer', + $name, + $class + ); + return $this; + } + + /** + * Register a random number source for this factory instance + * + * Note that this class must implement the Source interface + * + * @param string $name The name of the stategy + * @param string $class The class name of the implementation + * + * @return Factory $this The current factory instance + */ + public function registerSource($name, $class) { + $this->registerType( + 'sources', + __NAMESPACE__ . '\\Source', + $name, + $class + ); + return $this; + } + + /** + * Find a mixer based upon the requested strength + * + * @param Strength $strength The strength mixer to find + * + * @return Mixer The found mixer + * @throws RuntimeException if a valid mixer cannot be found + */ + protected function findMixer(\CryptLib\Core\Strength $strength) { + $newMixer = null; + $fallback = null; + foreach ($this->getMixers() as $mixer) { + if ($strength->compare($mixer::getStrength()) == 0) { + $newMixer = new $mixer; + } elseif ($strength->compare($mixer::getStrength()) == 1) { + $fallback = new $mixer; + } + } + if (is_null($newMixer)) { + if (is_null($fallback)) { + throw new \RuntimeException('Could not find mixer'); + } + return $fallback; + } + return $newMixer; + } + + /** + * Load all core mixing strategies + * + * @return void + */ + protected function loadMixers() { + $this->loadFiles( + __DIR__ . '/Mixer', + __NAMESPACE__ . '\\Mixer\\', + array($this, 'registerMixer') + ); + } + + /** + * Load all core random number sources + * + * @return void + */ + protected function loadSources() { + $this->loadFiles( + __DIR__ . '/Source', + __NAMESPACE__ . '\\Source\\', + array($this, 'registerSource') + ); + } + +} + diff --git a/CryptLib/Random/Generator.php b/CryptLib/Random/Generator.php new file mode 100644 index 0000000..b5509fa --- /dev/null +++ b/CryptLib/Random/Generator.php @@ -0,0 +1,186 @@ + + * @author Timo Hamina + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random; + +use CryptLib\Core\BaseConverter; + +/** + * The Random Number Generator Class + * + * Use this factory to generate cryptographic quality random numbers (strings) + * + * @category PHPCryptLib + * @package Random + * @author Anthony Ferrara + * @author Timo Hamina + */ +class Generator { + + /** + * @var Mixer The mixing strategy to use for this generator instance + */ + protected $mixer = null; + + /** + * @var array An array of random number sources to use for this generator + */ + protected $sources = array(); + + /** + * Build a new instance of the generator + * + * @param array $sources An array of random data sources to use + * @param Mixer $mixer The mixing strategy to use for this generator + */ + public function __construct(array $sources, Mixer $mixer) { + foreach ($sources as $source) { + $this->addSource($source); + } + $this->mixer = $mixer; + } + + /** + * Add a random number source to the generator + * + * @param Source $source The random number source to add + * + * @return Generator $this The current generator instance + */ + public function addSource(Source $source) { + $this->sources[] = $source; + return $this; + } + + /** + * Generate a random number (string) of the requested size + * + * @param int $size The size of the requested random number + * + * @return string The generated random number (string) + */ + public function generate($size) { + $seeds = array(); + foreach ($this->sources as $source) { + $seeds[] = $source->generate($size); + } + return $this->mixer->mix($seeds); + } + + /** + * Generate a random integer with the given range + * + * @param int $min The lower bound of the range to generate + * @param int $max The upper bound of the range to generate + * + * @return int The generated random number within the range + */ + public function generateInt($min = 0, $max = PHP_INT_MAX) { + $tmp = (int) max($max, $min); + $min = (int) min($max, $min); + $max = $tmp; + $range = $max - $min; + if ($range == 0) { + return $max; + } elseif ($range > PHP_INT_MAX || is_float($range)) { + /** + * This works, because PHP will auto-convert it to a float at this point, + * But on 64 bit systems, the float won't have enough precision to + * actually store the difference, so we need to check if it's a float + * and hence auto-converted... + */ + throw new \RangeException( + 'The supplied range is too great to generate' + ); + } + + $bits = (int) floor(log($range, 2) + 1); + $bytes = (int) max(ceil($bits / 8), 1); + $mask = (int) (pow(2, $bits) - 1); + /** + * The mask is a better way of dropping unused bits. Basically what it does + * is to set all the bits in the mask to 1 that we may need. Since the max + * range is PHP_INT_MAX, we will never need negative numbers (which would + * have the MSB set on the max int possible to generate). Therefore we + * can just mask that away. Since pow returns a float, we need to cast + * it back to an int so the mask will work. + * + * On a 64 bit platform, that means that PHP_INT_MAX is 2^63 - 1. Which + * is also the mask if 63 bits are needed (by the log(range, 2) call). + * So if the computed result is negative (meaning the 64th bit is set), the + * mask will correct that. + * + * This turns out to be slightly better than the shift as we don't need to + * worry about "fixing" negative values. + */ + do { + $test = $this->generate($bytes); + $result = hexdec(bin2hex($test)) & $mask; + } while ($result > $range); + return $result + $min; + } + + /** + * Generate a random string of specified length. + * + * This uses the supplied character list for generating the new result + * string. + * + * @param int $length The length of the generated string + * @param string $characters An optional list of characters to use + * + * @return string The generated random string + */ + public function generateString($length, $characters = '') { + if ($length == 0 || strlen($characters) == 1) { + return ''; + } elseif (empty($characters)) { + // Default to base 64 + $characters = '0123456789abcdefghijklmnopqrstuvwxyz' . + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ./'; + } + //determine how many bytes to generate + $bytes = ceil($length * floor(log(strlen($characters), 2) + 1.01) / 8); + $rand = $this->generate($bytes); + $result = BaseConverter::convertFromBinary($rand, $characters); + if (strlen($result) < $length) { + $result = str_pad($result, $length, $characters[0], STR_PAD_LEFT); + } else { + $result = substr($result, 0, $length); + } + return $result; + } + + /** + * Get the Mixer used for this instance + * + * @return Mixer the current mixer + */ + public function getMixer() { + return $this->mixer; + } + + /** + * Get the Sources used for this instance + * + * @return Source[] the current mixer + */ + public function getSources() { + return $this->sources; + } + +} diff --git a/CryptLib/Random/Mixer.php b/CryptLib/Random/Mixer.php new file mode 100644 index 0000000..7fab222 --- /dev/null +++ b/CryptLib/Random/Mixer.php @@ -0,0 +1,56 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random; + +/** + * The Mixer strategy interface. + * + * All mixing strategies must implement this interface + * + * @category PHPCryptLib + * @package Random + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +interface Mixer { + + /** + * Return an instance of Strength indicating the strength of the mixer + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength(); + + /** + * Test to see if the mixer is available + * + * @return boolean If the mixer is available on the system + */ + public static function test(); + + /** + * Mix the provided array of strings into a single output of the same size + * + * All elements of the array should be the same size. + * + * @param array $parts The parts to be mixed + * + * @return string The mixed result + */ + public function mix(array $parts); + +} diff --git a/CryptLib/Random/Mixer/DES.php b/CryptLib/Random/Mixer/DES.php new file mode 100644 index 0000000..2c4c287 --- /dev/null +++ b/CryptLib/Random/Mixer/DES.php @@ -0,0 +1,113 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Mixer; + +use \CryptLib\Cipher\Factory as CipherFactory; +use \CryptLib\Core\Strength; + +/** + * The DES medium strength mixer class + * + * This class implements a mixer based upon the recommendations in RFC 4086 + * section 5.2 + * + * @see http://tools.ietf.org/html/rfc4086#section-5.2 + * @category PHPCryptLib + * @package Random + * @subpackage Mixer + * @author Anthony Ferrara + */ +class DES extends \CryptLib\Random\AbstractMixer { + + /** + * An instance of a DES symmetric encryption cipher + * + * @var Cipher The DES cipher instance + */ + protected $cipher = 'des'; + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::MEDIUM); + } + + /** + * Test to see if the mixer is available + * + * @return boolean If the mixer is available on the system + */ + public static function test() { + return true; + } + + /** + * Build a new instance of the DES mixing function + * + * @param Factory $factory The optional encryption factory to use + * + * @return void + */ + public function __construct(\CryptLib\Cipher\Factory $factory = null) { + if (is_null($factory)) { + $factory = new CipherFactory(); + } + $this->cipher = $factory->getBlockCipher($this->cipher); + } + + /** + * Get the block size (the size of the individual blocks used for the mixing) + * + * @return int The block size + */ + protected function getPartSize() { + return $this->cipher->getBlockSize(); + } + + /** + * Mix 2 parts together using one method + * + * @param string $part1 The first part to mix + * @param string $part2 The second part to mix + * + * @return string The mixed data + */ + protected function mixParts1($part1, $part2) { + $this->cipher->setKey($part2); + return $this->cipher->encryptBlock($part1); + } + + /** + * Mix 2 parts together using another different method + * + * @param string $part1 The first part to mix + * @param string $part2 The second part to mix + * + * @return string The mixed data + */ + protected function mixParts2($part1, $part2) { + $this->cipher->setKey($part1); + return $this->cipher->decryptBlock($part2); + } + +} diff --git a/CryptLib/Random/Mixer/Hash.php b/CryptLib/Random/Mixer/Hash.php new file mode 100644 index 0000000..086d924 --- /dev/null +++ b/CryptLib/Random/Mixer/Hash.php @@ -0,0 +1,105 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Mixer; + +use \CryptLib\Core\Strength; + +/** + * The Hash medium strength mixer class + * + * This class implements a mixer based upon the recommendations in RFC 4086 + * section 5.2 + * + * @see http://tools.ietf.org/html/rfc4086#section-5.2 + * @category PHPCryptLib + * @package Random + * @subpackage Mixer + * @author Anthony Ferrara + */ +class Hash extends \CryptLib\Random\AbstractMixer { + + /** + * @var string The hash instance to use + */ + protected $hash = null; + + /** + * Build the hash mixer + * + * @param string $hash The hash instance to use (defaults to sha512) + * + * @return void + */ + public function __construct($hash = 'sha512') { + $this->hash = $hash; + } + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::LOW); + } + + /** + * Test to see if the mixer is available + * + * @return boolean If the mixer is available on the system + */ + public static function test() { + return true; + } + + /** + * Get the block size (the size of the individual blocks used for the mixing) + * + * @return int The block size + */ + protected function getPartSize() { + return strlen(hash($this->hash, '', true)); + } + + /** + * Mix 2 parts together using one method + * + * @param string $part1 The first part to mix + * @param string $part2 The second part to mix + * + * @return string The mixed data + */ + protected function mixParts1($part1, $part2) { + return hash_hmac($this->hash, $part1, $part2, true); + } + + /** + * Mix 2 parts together using another different method + * + * @param string $part1 The first part to mix + * @param string $part2 The second part to mix + * + * @return string The mixed data + */ + protected function mixParts2($part1, $part2) { + return hash_hmac($this->hash, $part2, $part1, true); + } + +} diff --git a/CryptLib/Random/Mixer/Rijndael.php b/CryptLib/Random/Mixer/Rijndael.php new file mode 100644 index 0000000..7114fbc --- /dev/null +++ b/CryptLib/Random/Mixer/Rijndael.php @@ -0,0 +1,55 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Mixer; + +use \CryptLib\Cipher\Factory as CipherFactory; +use \CryptLib\Core\Strength; + +/** + * The Rijndael-128 based high strength mixer class + * + * This class implements a mixer based upon the recommendations in RFC 4086 + * section 5.2 + * + * @see http://tools.ietf.org/html/rfc4086#section-5.2 + * @category PHPCryptLib + * @package Random + * @subpackage Mixer + * @author Anthony Ferrara + */ +class Rijndael extends DES { + + /** + * An instance of a Rijndael symmetric encryption cipher + * + * @var Cipher The Rijndael cipher instance + */ + protected $cipher = 'rijndael-128'; + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::HIGH); + } + +} diff --git a/CryptLib/Random/Source.php b/CryptLib/Random/Source.php new file mode 100644 index 0000000..89eaf66 --- /dev/null +++ b/CryptLib/Random/Source.php @@ -0,0 +1,50 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random; + +/** + * The Random Number Source interface. + * + * All random number sources must implement this interface + * + * @category PHPCryptLib + * @package Random + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +interface Source { + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength(); + + /** + * Generate a random string of the specified size + * + * Note: If the source fails to generate enough data, the result must be + * padded to the requested length. + * + * @param int $size The size of the requested random string + * + * @return string A string of the requested size + */ + public function generate($size); + +} diff --git a/CryptLib/Random/Source/CAPICOM.php b/CryptLib/Random/Source/CAPICOM.php new file mode 100644 index 0000000..8d6c98d --- /dev/null +++ b/CryptLib/Random/Source/CAPICOM.php @@ -0,0 +1,65 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Source; + +use CryptLib\Core\Strength; + +/** + * The Capicom Random Number Source + * + * This uses the Windows CapiCom Com object to generate random numbers + * + * @category PHPCryptLib + * @package Random + * @subpackage Source + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +class CAPICOM implements \CryptLib\Random\Source { + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::MEDIUM); + } + + /** + * Generate a random string of the specified size + * + * @param int $size The size of the requested random string + * + * @return string A string of the requested size + */ + public function generate($size) { + if (!class_exists('\\COM', false)) { + return str_repeat(chr(0), $size); + } + try { + $util = new \COM('CAPICOM.Utilities.1'); + $data = base64_decode($util->GetRandom($size, 0)); + return str_pad($data, $size, chr(0)); + } catch (\Exception $e) { + unset($e); + return str_repeat(chr(0), $size); + } + } + +} diff --git a/CryptLib/Random/Source/MTRand.php b/CryptLib/Random/Source/MTRand.php new file mode 100644 index 0000000..e96cbba --- /dev/null +++ b/CryptLib/Random/Source/MTRand.php @@ -0,0 +1,68 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Source; + +use CryptLib\Core\Strength; + +/** + * The MTRand Random Number Source + * + * This source generates low strength random numbers by using the internal + * mt_rand() function. By itself it is quite weak. However when combined with + * other sources it does provide significant benefit. + * + * @category PHPCryptLib + * @package Random + * @subpackage Source + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +class MTRand implements \CryptLib\Random\Source { + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + // Detect if Suhosin Hardened PHP patch is applied + if (defined('S_ALL')) { + return new Strength(Strength::MEDIUM); + } else { + return new Strength(Strength::LOW); + } + } + + /** + * Generate a random string of the specified size + * + * @param int $size The size of the requested random string + * + * @return string A string of the requested size + */ + public function generate($size) { + $result = ''; + for ($i = 0; $i < $size; $i++) { + $result .= chr((mt_rand() ^ mt_rand()) % 256); + } + return $result; + } + +} diff --git a/CryptLib/Random/Source/MicroTime.php b/CryptLib/Random/Source/MicroTime.php new file mode 100644 index 0000000..b4ae1f0 --- /dev/null +++ b/CryptLib/Random/Source/MicroTime.php @@ -0,0 +1,91 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Source; + +use CryptLib\Core\Strength; + +/** + * The Microtime Random Number Source + * + * This uses the current micro-second (looped several times) for a **very** weak + * random number source. This is only useful when combined with several other + * stronger sources + * + * @category PHPCryptLib + * @package Random + * @subpackage Source + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +class MicroTime implements \CryptLib\Random\Source { + + private $state = null; + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::VERYLOW); + } + + public function __construct() { + $state = ''; + if (function_exists('posix_times')) { + $state .= serialize(posix_times()); + } + $state .= getmypid() . memory_get_usage(); + $state .= serialize($_ENV); + $this->state = hash('sha512', $state, true); + } + + /** + * Generate a random string of the specified size + * + * @param int $size The size of the requested random string + * + * @return string A string of the requested size + */ + public function generate($size) { + $result = ''; + $seed = microtime() . memory_get_usage(); + $this->state = hash('sha512', $this->state . $seed, true); + /** + * Make the generated randomness a bit better by forcing a GC run which + * should complete in a indeterminate amount of time, hence improving + * the strength of the randomness a bit. It's still not crypto-safe, + * but at least it's more difficult to predict. + */ + gc_collect_cycles(); + for ($i = 0; $i < $size; $i += 8) { + $seed = $this->state . microtime() . pack('N', $i); + $this->state = hash('sha512', $seed, true); + /** + * We only use the first 8 bytes here to prevent exposing the state + * in its entirety, which could potentially expose other random + * generations in the future (in the same process)... + */ + $result .= substr($this->state, 0, 8); + } + return substr($result, 0, $size); + } + +} diff --git a/CryptLib/Random/Source/OpenSSL.php b/CryptLib/Random/Source/OpenSSL.php new file mode 100644 index 0000000..87dd333 --- /dev/null +++ b/CryptLib/Random/Source/OpenSSL.php @@ -0,0 +1,64 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Source; + +use CryptLib\Core\Strength; + +/** + * The OpenSSL Random Number Source + * + * This uses the OS's secure generator to generate high strength numbers + * + * @category PHPCryptLib + * @package Random + * @subpackage Source + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +class OpenSSL implements \CryptLib\Random\Source { + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::HIGH); + } + + /** + * Generate a random string of the specified size + * + * @param int $size The size of the requested random string + * + * @return string A string of the requested size + */ + public function generate($size) { + if (!function_exists('openssl_random_pseudo_bytes') || $size < 1) { + return str_repeat(chr(0), $size); + } + /** + * Note, normally we would check the return of of $crypto_strong to + * ensure that we generated a good random string. However, since we're + * using this as one part of many sources a low strength random number + * shouldn't be much of an issue. + */ + return openssl_random_pseudo_bytes($size); + } + +} diff --git a/CryptLib/Random/Source/Rand.php b/CryptLib/Random/Source/Rand.php new file mode 100644 index 0000000..20e2c61 --- /dev/null +++ b/CryptLib/Random/Source/Rand.php @@ -0,0 +1,68 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Source; + +use CryptLib\Core\Strength; + +/** + * The Rand Random Number Source + * + * This source generates low strength random numbers by using the internal + * rand() function. By itself it is quite weak. However when combined with + * other sources it does provide significant benefit. + * + * @category PHPCryptLib + * @package Random + * @subpackage Source + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +class Rand implements \CryptLib\Random\Source { + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + // Detect if Suhosin Hardened PHP patch is applied + if (defined('S_ALL')) { + return new Strength(Strength::LOW); + } else { + return new Strength(Strength::VERYLOW); + } + } + + /** + * Generate a random string of the specified size + * + * @param int $size The size of the requested random string + * + * @return string A string of the requested size + */ + public function generate($size) { + $result = ''; + for ($i = 0; $i < $size; $i++) { + $result .= chr((rand() ^ rand()) % 256); + } + return $result; + } + +} diff --git a/CryptLib/Random/Source/Random.php b/CryptLib/Random/Source/Random.php new file mode 100644 index 0000000..aaedd24 --- /dev/null +++ b/CryptLib/Random/Source/Random.php @@ -0,0 +1,49 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Source; + +use CryptLib\Core\Strength; + +/** + * The Random Random Number Source + * + * This uses the *nix /dev/random device to generate high strength numbers + * + * @category PHPCryptLib + * @package Random + * @subpackage Source + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +class Random extends URandom { + + /** + * @var string The file to read from + */ + protected $file = '/dev/random'; + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::HIGH); + } + +} diff --git a/CryptLib/Random/Source/URandom.php b/CryptLib/Random/Source/URandom.php new file mode 100644 index 0000000..5db7056 --- /dev/null +++ b/CryptLib/Random/Source/URandom.php @@ -0,0 +1,72 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Source; + +use CryptLib\Core\Strength; + +/** + * The URandom Random Number Source + * + * This uses the *nix /dev/urandom device to generate medium strength numbers + * + * @category PHPCryptLib + * @package Random + * @subpackage Source + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +class URandom implements \CryptLib\Random\Source { + + /** + * @var string The file to read from + */ + protected $file = '/dev/urandom'; + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::MEDIUM); + } + + /** + * Generate a random string of the specified size + * + * @param int $size The size of the requested random string + * + * @return string A string of the requested size + */ + public function generate($size) { + if ($size == 0 || !file_exists($this->file)) { + return str_repeat(chr(0), $size); + } + $file = fopen($this->file, 'rb'); + if (!$file) { + return str_repeat(chr(0), $size); + } + if (function_exists('stream_set_read_buffer')) { + stream_set_read_buffer($file, 0); + } + $result = fread($file, $size); + fclose($file); + return $result; + } + +} diff --git a/CryptLib/Random/Source/UniqID.php b/CryptLib/Random/Source/UniqID.php new file mode 100644 index 0000000..02418ed --- /dev/null +++ b/CryptLib/Random/Source/UniqID.php @@ -0,0 +1,61 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib\Random\Source; + +use CryptLib\Core\Strength; + +/** + * The UniqID Random Number Source + * + * This uses the internal `uniqid()` function to generate low strength random + * numbers. + * + * @category PHPCryptLib + * @package Random + * @subpackage Source + * @author Anthony Ferrara + * @codeCoverageIgnore + */ +class UniqID implements \CryptLib\Random\Source { + + /** + * Return an instance of Strength indicating the strength of the source + * + * @return Strength An instance of one of the strength classes + */ + public static function getStrength() { + return new Strength(Strength::LOW); + } + + /** + * Generate a random string of the specified size + * + * @param int $size The size of the requested random string + * + * @return string A string of the requested size + */ + public function generate($size) { + $result = ''; + while (strlen($result) < $size) { + $result = uniqid($result, true); + } + return substr($result, 0, $size); + } + +} diff --git a/CryptLib/bootstrap.php b/CryptLib/bootstrap.php new file mode 100644 index 0000000..a333926 --- /dev/null +++ b/CryptLib/bootstrap.php @@ -0,0 +1,25 @@ + + * @copyright 2011 The Authors + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version Build @@version@@ + */ + +namespace CryptLib; + +require_once __DIR__ . '/Core/AutoLoader.php'; + +$autoloader = new Core\AutoLoader(__NAMESPACE__, dirname(__DIR__)); + +$autoloader->register(); \ No newline at end of file diff --git a/speedport.class.php b/speedport.class.php index 83c6b63..7bc5e8b 100644 --- a/speedport.class.php +++ b/speedport.class.php @@ -123,6 +123,7 @@ class speedport { $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'reboot_device' => 'true'); $cookie = 'challengev='.$this->challenge.'; '.$this->session; $data = $this->sentRequest($path, $fields, $cookie); + $json = json_decode($data['body'], true); return $json; @@ -309,6 +310,52 @@ class speedport { return $json; } + /** + * decrypt data from router + * + * @param string $data + * @return array + */ + public function decrypt ($data) { + require_once 'CryptLib/CryptLib.php'; + $factory = new CryptLib\Cipher\Factory(); + $aes = $factory->getBlockCipher('rijndael-128'); + + $iv = hex2bin(substr($this->challenge, 16, 16)); + $adata = hex2bin(substr($this->challenge, 32, 16)); + $dkey = hex2bin($this->derivedk); + $enc = hex2bin($data); + + $aes->setKey($dkey); + $mode = $factory->getMode('ccm', $aes, $iv, [ 'adata' => $adata, 'lSize' => 7]); + + $mode->decrypt($enc); + + return $mode->finish(); + } + + /** + * decrypt data for the router + * + * @param array $data + * @return string + */ + public function encrypt ($data) { + require_once 'CryptLib/CryptLib.php'; + $factory = new CryptLib\Cipher\Factory(); + $aes = $factory->getBlockCipher('rijndael-128'); + + $iv = hex2bin(substr($this->challenge, 16, 16)); + $adata = hex2bin(substr($this->challenge, 32, 16)); + $dkey = hex2bin($this->derivedk); + + $aes->setKey($dkey); + $mode = $factory->getMode('ccm', $aes, $iv, [ 'adata' => $adata, 'lSize' => 7]); + $mode->encrypt(http_build_query($data)); + + return $mode->finish(); + } + /** * sends the request to router * @@ -347,6 +394,7 @@ class speedport { curl_close($ch); // fix invalid json + $body = preg_replace("/(\r\n)|(\r)/", "\n", $body); $body = preg_replace('/\'/i', '"', $body); $body = preg_replace("/\[\s+\]/i", '[ {} ]', $body);