From 4cc314b4674568bc684e639d09f3ce275dcf5124 Mon Sep 17 00:00:00 2001 From: mmeyer2k Date: Sun, 25 Oct 2020 16:56:57 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=83=F0=9F=91=BB=2013.1.2=20spooky=20up?= =?UTF-8?q?date?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .circleci/config.yml | 1 - README.md | 29 ++++-- composer.json | 56 ++++++------ docs/CHANGELOG.md | 12 ++- examples/classes/Aes256Base64.php | 18 ---- examples/classes/TinyFish.php | 29 ------ examples/support.php | 60 ------------- src/Exceptions/InvalidChecksumException.php | 6 +- ...on.php => InvalidKeyEncodingException.php} | 7 +- src/Exceptions/InvalidKeyLengthException.php | 35 ++++++++ src/OneTimePad.php | 8 +- src/OpensslBridge.php | 6 ++ src/OpensslKey.php | 88 +++++++++++++------ src/OpensslStack.php | 8 +- src/OpensslStatic.php | 51 ++++------- src/OpensslWrapper.php | 62 ++++++------- src/Str.php | 48 ++++++---- tests/AesBase.php | 16 ++-- tests/OpensslKeyTest.php | 11 ++- tests/OpensslStaticTest.php | 29 +++--- {examples => tests}/vectors.php | 4 +- 21 files changed, 295 insertions(+), 289 deletions(-) delete mode 100644 examples/classes/Aes256Base64.php delete mode 100644 examples/classes/TinyFish.php delete mode 100644 examples/support.php rename src/Exceptions/{InvalidKeyException.php => InvalidKeyEncodingException.php} (78%) create mode 100644 src/Exceptions/InvalidKeyLengthException.php rename {examples => tests}/vectors.php (90%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 620152b7..df8f3263 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,6 @@ jobs: - run: ./vendor/bin/phpunit --coverage-html coverage --coverage-clover=coverage.clover - run: ./vendor/bin/infection - run: ./vendor/bin/phpmetrics --report-html=phpmetrics ./src - - run: php examples/support.php - run: wget https://scrutinizer-ci.com/ocular.phar - run: php ocular.phar code-coverage:upload --format=php-clover coverage.clover - save_cache: diff --git a/README.md b/README.md index d1c58364..97993fc4 100644 --- a/README.md +++ b/README.md @@ -36,18 +36,28 @@ composer require "mmeyer2k/dcrypt=^13.0" ## Block Ciphers -The dcrypt library helps application developers avoid common mistakes in crypto implementations that leave data at risk while still providing flexibility in its options for crypto enthusiasts. -Dcrypt's block cipher functions require the use of a high entropy 256 bit (minimum), base64-encoded key. +The dcrypt library helps application developers avoid common mistakes in crypto implementations that leave data at risk. -To generate a new key, execute this on the command line: +[Specification document](https://github.com/mmeyer2k/dcrypt/blob/master/docs/CRYPTO.md) + +### Keys + +Safe usage of dcrypt's block cipher functions requires the use of a high entropy 256 bit (minimum) key. +Keys should be passed into dcrypt in *base64* encoded format. + +**You are responsible for the randomness of your key!** + +Generate a new key on the linux CLI: ```bash head -c 32 /dev/urandom | base64 -w 0 | xargs echo ``` -_You are responsible for the randomness of your key!_ - -[Specification document](https://github.com/mmeyer2k/dcrypt/blob/master/docs/CRYPTO.md) +Or with PHP... +```php +=7.1.0" - }, - "autoload": { - "psr-4": { - "Dcrypt\\": "src/" - } - }, - "autoload-dev": { - "classmap": [ - "examples/", - "tests/" - ] - }, - "minimum-stability": "dev" + "name": "mmeyer2k/dcrypt", + "description": "A petite library of encryption functionality for PHP", + "keywords": [ + "encryption", + "aes", + "gcm", + "openssl" + ], + "license": "MIT", + "authors": [ + { + "name": "Michael Meyer", + "email": "m.meyer2k@gmail.com" + } + ], + "require": { + "php": ">=7.1.0", + "ext-openssl": "*", + "ext-mbstring": "*" + }, + "autoload": { + "psr-4": { + "Dcrypt\\": "src/" + } + }, + "autoload-dev": { + "classmap": [ + "tests/" + ] + }, + "minimum-stability": "dev" } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6e084427..3099f21b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,4 +1,14 @@ -# Changes in Dcrypt +# Changelog for `dcrypt` + +## 13.1.2 +- Offload some functionality into OpensslKey object for readability +- OpensslKey throws exception if non-allowed properties are accessed +- Add ext-openssl and ext-mbstring to the requirements (makes IDE happy) +- Remove examples directory in favor of a more robust `docs/` option +- Improved exception handling that sheds some legacy crust +- Removed superfluous root namespace backslashes throughout project +- Modified ciphertext unpacking algorithm +- More complete docblocks ## 13.1.1 - Add test class for `Aes` static helper object diff --git a/examples/classes/Aes256Base64.php b/examples/classes/Aes256Base64.php deleted file mode 100644 index 34262d48..00000000 --- a/examples/classes/Aes256Base64.php +++ /dev/null @@ -1,18 +0,0 @@ -getMessage(); - echo ' [fail] [!!!]'; - } finally { - echo "\n"; - } -} - -echo "\nALGOS ------------------------------------------------------------------------------------------------\n"; - -foreach (\hash_algos() as $algo) { - // Only process the lower case names - if (\strtolower($algo) !== $algo) { - continue; - } - - echo \str_pad("[$algo]", 40); - - try { - $e = \Dcrypt\OpensslStatic::encrypt('AAAA', $key, 'aes-256-gcm', $algo); - $d = \Dcrypt\OpensslStatic::decrypt($e, $key, 'aes-256-gcm', $algo); - - echo ' [pass] '; - } catch (\Exception | \Error $e) { - $m = $e->getMessage(); - echo ' [fail] [!!!]'; - } finally { - echo "\n"; - } -} diff --git a/src/Exceptions/InvalidChecksumException.php b/src/Exceptions/InvalidChecksumException.php index cbbf45e7..299da877 100644 --- a/src/Exceptions/InvalidChecksumException.php +++ b/src/Exceptions/InvalidChecksumException.php @@ -17,6 +17,8 @@ namespace Dcrypt\Exceptions; +use Exception; + /** * A handler for checksum exceptions. * @@ -27,7 +29,7 @@ * * @link https://github.com/mmeyer2k/dcrypt */ -class InvalidChecksumException extends \Exception +class InvalidChecksumException extends Exception { - const MESSAGE = 'Invalid ciphertext checksum'; + protected $message = 'Invalid ciphertext checksum'; } diff --git a/src/Exceptions/InvalidKeyException.php b/src/Exceptions/InvalidKeyEncodingException.php similarity index 78% rename from src/Exceptions/InvalidKeyException.php rename to src/Exceptions/InvalidKeyEncodingException.php index 183fc1d4..17f9e994 100644 --- a/src/Exceptions/InvalidKeyException.php +++ b/src/Exceptions/InvalidKeyEncodingException.php @@ -17,6 +17,8 @@ namespace Dcrypt\Exceptions; +use Exception; + /** * A handler for key exceptions. * @@ -27,8 +29,7 @@ * * @link https://github.com/mmeyer2k/dcrypt */ -class InvalidKeyException extends \Exception +class InvalidKeyEncodingException extends Exception { - const KEYLENGTH = 'Key must be at least 32 bytes'; - const BASE64ENC = 'Key must be properly formatted base64'; + protected $message = 'Key must be base64 encoded'; } diff --git a/src/Exceptions/InvalidKeyLengthException.php b/src/Exceptions/InvalidKeyLengthException.php new file mode 100644 index 00000000..384789cd --- /dev/null +++ b/src/Exceptions/InvalidKeyLengthException.php @@ -0,0 +1,35 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Dcrypt\Exceptions; + +use Exception; + +/** + * A handler for key exceptions. + * + * @category Dcrypt + * + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * + * @link https://github.com/mmeyer2k/dcrypt + */ +class InvalidKeyLengthException extends Exception +{ + protected $message = 'Key must be at least 32 bytes'; +} diff --git a/src/OneTimePad.php b/src/OneTimePad.php index c9a300d9..4b586c6d 100644 --- a/src/OneTimePad.php +++ b/src/OneTimePad.php @@ -37,6 +37,8 @@ class OneTimePad * @param string $key Encryption/decryption key to use on input * @param string $algo Hashing algo to generate keystream * + * @throws Exceptions\InvalidKeyLengthException + * * @return string */ public static function crypt( @@ -45,13 +47,13 @@ public static function crypt( string $algo = 'sha3-512' ): string { // Split the input into chunks sized the same as the hash size - $chunks = \str_split($input, Str::hashSize($algo)); + $chunks = str_split($input, Str::hashSize($algo)); // Determine total input length $length = Str::strlen($input); // Create a new key object - $key = new OpensslKey($algo, $key); + $key = new OpensslKey($key, $algo); foreach ($chunks as $i => &$chunk) { // Create the info key based on counter @@ -61,6 +63,6 @@ public static function crypt( $chunk = $chunk ^ $key->deriveKey($info); } - return \implode($chunks); + return implode($chunks); } } diff --git a/src/OpensslBridge.php b/src/OpensslBridge.php index 4009d00e..2c342998 100644 --- a/src/OpensslBridge.php +++ b/src/OpensslBridge.php @@ -17,6 +17,8 @@ namespace Dcrypt; +use Exception; + /** * Provides functionality common to the dcrypt AES block ciphers. * Extend this class to customize your cipher suite. @@ -36,6 +38,8 @@ class OpensslBridge * @param string $data Ciphertext to decrypt * @param string $key Key which will be used to decrypt data * + * @throws Exception + * * @return string */ public static function decrypt(string $data, string $key): string @@ -49,6 +53,8 @@ public static function decrypt(string $data, string $key): string * @param string $data Plaintext string to encrypt. * @param string $key Key which will be used to encrypt data * + * @throws Exception + * * @return string */ public static function encrypt(string $data, string $key): string diff --git a/src/OpensslKey.php b/src/OpensslKey.php index 6cee0a58..1ab29577 100644 --- a/src/OpensslKey.php +++ b/src/OpensslKey.php @@ -17,7 +17,9 @@ namespace Dcrypt; -use Dcrypt\Exceptions\InvalidKeyException; +use Dcrypt\Exceptions\InvalidKeyEncodingException; +use Dcrypt\Exceptions\InvalidKeyLengthException; +use Exception; /** * Provides key derivation functions. @@ -50,64 +52,73 @@ final class OpensslKey * * @var string */ - private $_ivr; + private $_iv; + + /** + * Name of cipher. + * + * @var string + */ + private $_cipher; /** * OpensslKey constructor. * - * @param string $algo Algo to use for HKDF - * @param string $key Key to use for encryption - * @param string $ivr Initialization vector + * @param string $key Key to use for encryption + * @param string $algo Algo to use for HKDF + * @param string $cipher Name of cipher + * @param string $iv Initialization vector * - * @throws InvalidKeyException + * @throws InvalidKeyLengthException + * @throws InvalidKeyEncodingException */ public function __construct( - string $algo, string $key, - string $ivr = '' + string $algo, + string $cipher = '', + string $iv = '' ) { // Store the key as what was supplied - $this->_key = \base64_decode($key, true); + $this->_key = base64_decode($key, true); // If key was not proper base64, bail out if ($this->_key === false) { - throw new InvalidKeyException(InvalidKeyException::BASE64ENC); + throw new InvalidKeyEncodingException(); } // If key was to short, bail out if (Str::strlen($this->_key) < 32) { - throw new InvalidKeyException(InvalidKeyException::KEYLENGTH); + throw new InvalidKeyLengthException(); } // Store algo in object $this->_algo = $algo; // Store init vector in object - $this->_ivr = $ivr; + $this->_iv = $iv; + + // Store the cipher name + $this->_cipher = $cipher; } /** * Generate the authentication key. * - * @param string $info The extra info parameter for hash_hkdf - * * @return string */ - public function authenticationKey(string $info): string + public function authenticationKey(): string { - return $this->deriveKey(__FUNCTION__ . '|' . $info); + return $this->deriveKey(__FUNCTION__ . '|' . $this->_cipher); } /** * Generate the encryption key. * - * @param string $info The extra info parameter for hash_hkdf - * * @return string */ - public function encryptionKey(string $info): string + public function encryptionKey(): string { - return $this->deriveKey(__FUNCTION__ . '|' . $info); + return $this->deriveKey(__FUNCTION__ . '|' . $this->_cipher); } /** @@ -119,24 +130,51 @@ public function encryptionKey(string $info): string */ public function deriveKey(string $info): string { - return \hash_hkdf($this->_algo, $this->_key, 0, $info, $this->_ivr); + return hash_hkdf($this->_algo, $this->_key, 0, $info, $this->_iv); + } + + /** + * Calculates a given message HMAC. + * + * @param string $message + * + * @return string + */ + public function messageChecksum(string $message): string + { + return hash_hmac($this->_algo, $message, $this->authenticationKey(), true); + } + + /** + * Allows read only access to the internal variables needed by the openssl wrapper. + * + * @return array + */ + public function wrapperVariables(): array + { + return [ + $this->_iv, + $this->encryptionKey(), + $this->_cipher, + ]; } /** - * Generate a new key that meets requirements for dcrypt. + * Generate a new key. * * @param int $bytes Size of key in bytes * - * @throws InvalidKeyException + * @throws Exception + * @throws InvalidKeyLengthException * * @return string */ public static function create(int $bytes = 32): string { if ($bytes < 32) { - throw new InvalidKeyException(InvalidKeyException::KEYLENGTH); + throw new InvalidKeyLengthException(); } - return \base64_encode(\random_bytes($bytes)); + return base64_encode(random_bytes($bytes)); } } diff --git a/src/OpensslStack.php b/src/OpensslStack.php index 835af8fe..d06aa845 100644 --- a/src/OpensslStack.php +++ b/src/OpensslStack.php @@ -17,6 +17,8 @@ namespace Dcrypt; +use Exception; + /** * A factory class to build and use custom encryption stacks. * @@ -73,6 +75,8 @@ public function add(string $cipher, string $algo): self * * @param string $data Data to encrypt * + * @throws Exception + * * @return string */ public function encrypt(string $data): string @@ -89,11 +93,13 @@ public function encrypt(string $data): string * * @param string $data Data to decrypt * + * @throws Exception + * * @return string */ public function decrypt(string $data): string { - foreach (\array_reverse($this->_stack) as $s) { + foreach (array_reverse($this->_stack) as $s) { $data = OpensslStatic::decrypt($data, $this->_key, $s[0], $s[1]); } diff --git a/src/OpensslStatic.php b/src/OpensslStatic.php index c2043fd1..c632834d 100644 --- a/src/OpensslStatic.php +++ b/src/OpensslStatic.php @@ -18,6 +18,7 @@ namespace Dcrypt; use Dcrypt\Exceptions\InvalidChecksumException; +use Exception; /** * Static functions that handle encryption/decryption with openssl. @@ -39,7 +40,7 @@ final class OpensslStatic extends OpensslWrapper * @param string $cipher OpenSSL cipher name * @param string $algo Hash algo name * - * @throws \Exception + * @throws Exception * * @return string */ @@ -49,43 +50,28 @@ public static function decrypt( string $cipher, string $algo ): string { - // Calculate the hash checksum size in bytes for the specified algo - $hsz = Str::hashSize($algo); + // Shift the IV off of the beginning of the ciphertext + $ivr = Str::shift($data, parent::ivSize($cipher)); - // Get the tag size in bytes for this cipher mode - $tsz = parent::tagRequired($cipher) ? 16 : 0; + // Shift off the checksum + $sum = Str::shift($data, Str::hashSize($algo)); - // Ask openssl for the IV size needed for specified cipher - $isz = parent::ivSize($cipher); + // Shift off the AAD tag (if present) + $tag = Str::shift($data, parent::tagLength($cipher)); - // Get the IV at the beginning of the ciphertext - $ivr = Str::substr($data, 0, $isz); - - // Get the checksum after the IV - $sum = Str::substr($data, $isz, $hsz); - - // Get the AEAD authentication tag (if present) after the checksum - $tag = Str::substr($data, $isz + $hsz, $tsz); - - // Get the encrypted message payload - $msg = Str::substr($data, $isz + $hsz + $tsz); - - // Create key derivation object - $key = new OpensslKey($algo, $key, $ivr); + // Create a new key object + $key = new OpensslKey($key, $algo, $cipher, $ivr); // Calculate checksum of message payload for verification - $chk = \hash_hmac($algo, $msg, $key->authenticationKey($cipher), true); + $chk = $key->messageChecksum($data); // Compare given checksum against computed checksum if (!Str::equal($chk, $sum)) { - throw new InvalidChecksumException(InvalidChecksumException::MESSAGE); + throw new InvalidChecksumException(); } - // Derive the encryption key - $enc = $key->encryptionKey($cipher); - // Decrypt message and return - return parent::opensslDecrypt($msg, $cipher, $enc, $ivr, $tag); + return parent::opensslDecrypt($data, $key, $tag); } /** @@ -96,7 +82,7 @@ public static function decrypt( * @param string $cipher OpenSSL cipher name * @param string $algo Hash algo name * - * @throws \Exception + * @throws Exception * * @return string */ @@ -110,19 +96,16 @@ public static function encrypt( $ivr = parent::ivGenerate($cipher); // Create key derivation object - $key = new OpensslKey($algo, $key, $ivr); + $key = new OpensslKey($key, $algo, $cipher, $ivr); // Create a variable for the authentication tag to be returned by reference $tag = ''; - // Derive the encryption key - $enc = $key->encryptionKey($cipher); - // Encrypt the plaintext - $msg = parent::opensslEncrypt($data, $cipher, $enc, $ivr, $tag); + $msg = parent::opensslEncrypt($data, $key, $tag); // Generate the ciphertext checksum - $chk = \hash_hmac($algo, $msg, $key->authenticationKey($cipher), true); + $chk = $key->messageChecksum($msg); // Return concatenation of iv + checksum + tag + ciphertext return $ivr . $chk . $tag . $msg; diff --git a/src/OpensslWrapper.php b/src/OpensslWrapper.php index c1490307..1c7370d9 100644 --- a/src/OpensslWrapper.php +++ b/src/OpensslWrapper.php @@ -32,51 +32,41 @@ class OpensslWrapper /** * OpenSSL encrypt wrapper function. * - * @param string $data Data to decrypt - * @param string $cipher Cipher method to use - * @param string $key Key string - * @param string $iv Initialization vector - * @param string $tag AAD tag + * @param string $data Data to decrypt + * @param OpensslKey $key Key string + * @param string $tag AAD tag * * @return string */ - protected static function opensslEncrypt( - string $data, - string $cipher, - string $key, - string $iv, - string &$tag - ): string { - if (self::tagRequired($cipher)) { - return \openssl_encrypt($data, $cipher, $key, 1, $iv, $tag, '', 16); + protected static function opensslEncrypt(string $data, OpensslKey $key, string &$tag): string + { + list($iv, $enc, $cipher) = $key->wrapperVariables(); + + if (self::tagLength($cipher) > 0) { + return openssl_encrypt($data, $cipher, $enc, 1, $iv, $tag, '', 16); } - return \openssl_encrypt($data, $cipher, $key, 1, $iv); + return openssl_encrypt($data, $cipher, $enc, 1, $iv); } /** * OpenSSL decrypt wrapper function. * - * @param string $input Data to decrypt - * @param string $cipher Cipher method to use - * @param string $key Key string - * @param string $iv Initialization vector - * @param string $tag AAD authentication tag + * @param string $input Data to decrypt + * @param OpensslKey $key Key string + * @param string $tag AAD authentication tag * * @return string */ - protected static function opensslDecrypt( - string $input, - string $cipher, - string $key, - string $iv, - string $tag - ): string { - if (self::tagRequired($cipher)) { - return \openssl_decrypt($input, $cipher, $key, 1, $iv, $tag, ''); + protected static function opensslDecrypt(string $input, OpensslKey $key, string $tag): string + { + list($iv, $enc, $cipher) = $key->wrapperVariables(); + + if (self::tagLength($cipher) > 0) { + return openssl_decrypt($input, $cipher, $enc, 1, $iv, $tag, ''); } - return \openssl_decrypt($input, $cipher, $key, 1, $iv); + return openssl_decrypt($input, $cipher, $enc, 1, $iv); } /** @@ -88,7 +78,7 @@ protected static function opensslDecrypt( */ protected static function ivSize(string $cipher): int { - return \openssl_cipher_iv_length($cipher); + return openssl_cipher_iv_length($cipher); } /** @@ -108,7 +98,7 @@ protected static function ivGenerate(string $cipher): string return ''; } - return \random_bytes($size); + return random_bytes($size); } /** @@ -116,12 +106,10 @@ protected static function ivGenerate(string $cipher): string * * @param string $cipher Openssl cipher * - * @return bool + * @return int */ - protected static function tagRequired(string $cipher): bool + protected static function tagLength(string $cipher): int { - $cipher = strtolower($cipher); - - return strpos($cipher, '-gcm') || strpos($cipher, '-ccm'); + return stripos($cipher, '-gcm') || stripos($cipher, '-ccm') ? 16 : 0; } } diff --git a/src/Str.php b/src/Str.php index c67665ee..4285a3ae 100644 --- a/src/Str.php +++ b/src/Str.php @@ -17,6 +17,8 @@ namespace Dcrypt; +use Exception; + /** * Provides time-safe string comparison facilities, and safe string operations * on systems that have mb_* function overloading enabled. @@ -42,18 +44,20 @@ final class Str * @param string $known The string of known length to compare against * @param string $given The string that the user can control * + * @throws Exception + * * @return bool */ public static function equal(string $known, string $given): bool { // Create some entropy - $nonce = \random_bytes(16); + $nonce = random_bytes(16); // Prehash the input strings with the nonce - $known = \hash_hmac('sha256', $known, $nonce, true); - $given = \hash_hmac('sha256', $given, $nonce, true); + $known = hash_hmac('sha256', $known, $nonce, true); + $given = hash_hmac('sha256', $given, $nonce, true); - return \hash_equals($known, $given); + return hash_equals($known, $given); } /** @@ -65,7 +69,7 @@ public static function equal(string $known, string $given): bool */ public static function hashSize(string $algo): int { - return self::strlen(\hash($algo, 'hash me', true)); + return self::strlen(hash($algo, 'hash me', true)); } /** @@ -77,23 +81,37 @@ public static function hashSize(string $algo): int */ public static function strlen(string $string): int { - return \mb_strlen($string, '8bit'); + return mb_strlen($string, '8bit'); } /** * Returns part of a string. * - * @param string $string The string whose length we wish to obtain - * @param int $start Offset to start gathering output - * @param int $length Distance from starting offset to gather + * @param string $string The string whose length we wish to obtain + * @param int $start Offset to start gathering output + * @param int|null $length Distance from starting offset to gather + * + * @return string + */ + public static function substr(string $string, int $start, int $length = null): string + { + return mb_substr($string, $start, $length, '8bit'); + } + + /** + * Shifts bytes off of the front of a string and return. Input string is modified. + * + * @param string $input + * @param int $bytes * * @return string */ - public static function substr( - string $string, - int $start, - int $length = null - ): string { - return \mb_substr($string, $start, $length, '8bit'); + public static function shift(string &$input, int $bytes): string + { + $shift = self::substr($input, 0, $bytes); + + $input = self::substr($input, $bytes); + + return $shift; } } diff --git a/tests/AesBase.php b/tests/AesBase.php index dcc28e5e..2ead0cb1 100644 --- a/tests/AesBase.php +++ b/tests/AesBase.php @@ -4,11 +4,15 @@ namespace Dcrypt\Tests; +use Dcrypt\Exceptions\InvalidChecksumException; +use Dcrypt\Exceptions\InvalidKeyEncodingException; +use Dcrypt\OpensslKey; + class AesBase extends \PHPUnit\Framework\TestCase { public function testEngineInKeyMode() { - $key = \Dcrypt\OpensslKey::create(); + $key = OpensslKey::create(); $encrypted = static::$class::encrypt('a secret', $key); $decrypted = static::$class::decrypt($encrypted, $key); @@ -19,7 +23,7 @@ public function testEngineInKeyMode() public function testEngineWithSomeRandomnessWhileInKeyMode() { $input = random_bytes(256); - $key = \Dcrypt\OpensslKey::create(); + $key = OpensslKey::create(); $encrypted = static::$class::encrypt($input, $key); $decrypted = static::$class::decrypt($encrypted, $key); @@ -29,20 +33,20 @@ public function testEngineWithSomeRandomnessWhileInKeyMode() public function testCorruptDataUsingKeyMode() { - $key = \Dcrypt\OpensslKey::create(); + $key = OpensslKey::create(); $encrypted = static::$class::encrypt('a secret', $key); $this->assertEquals('a secret', static::$class::decrypt($encrypted, $key)); - $this->expectException(\Dcrypt\Exceptions\InvalidChecksumException::class); + $this->expectException(InvalidChecksumException::class); static::$class::decrypt($encrypted . 'A', $key); } public function testInvalidKeyEncoding() { - $this->expectException(\Dcrypt\Exceptions\InvalidKeyException::class); + $this->expectException(InvalidKeyEncodingException::class); $crazyKey = str_repeat('?', 10000); @@ -62,7 +66,7 @@ public function testNameMatch() public function testKnownVector() { // Skip if PHP 7.1 and CCM mode. Implementation in Openssl was fixed but never backported it seems... - if (PHP_MAJOR_VERSION === 7 && PHP_MINOR_VERSION === 1 && strpos(static::$class, 'Ccm')) { + if (PHP_MAJOR_VERSION . PHP_MINOR_VERSION === '71' && strpos(static::$class, 'Ccm')) { return $this->assertTrue(true); } diff --git a/tests/OpensslKeyTest.php b/tests/OpensslKeyTest.php index 57fe0dc7..7bae8b11 100644 --- a/tests/OpensslKeyTest.php +++ b/tests/OpensslKeyTest.php @@ -4,25 +4,28 @@ namespace Dcrypt\Tests; -use Dcrypt\Exceptions\InvalidKeyException; +use Dcrypt\Exceptions\InvalidKeyEncodingException; +use Dcrypt\Exceptions\InvalidKeyLengthException; use Dcrypt\OpensslKey; class OpensslKeyTest extends \PHPUnit\Framework\TestCase { public function testNewKeyTooShort() { + // A key with 32 chars should work... \Dcrypt\OpensslKey::create(32); - $this->expectException(InvalidKeyException::class); + $this->expectException(InvalidKeyLengthException::class); + // but 31 should not. \Dcrypt\OpensslKey::create(31); } public function testKeyInvalidBase64() { - $str = str_repeat('A', 32); + $str = str_repeat('.', 32); - $this->expectException(InvalidKeyException::class); + $this->expectException(InvalidKeyEncodingException::class); new OpensslKey('sha3-256', $str); } diff --git a/tests/OpensslStaticTest.php b/tests/OpensslStaticTest.php index 0cf0459e..ac25518d 100644 --- a/tests/OpensslStaticTest.php +++ b/tests/OpensslStaticTest.php @@ -4,7 +4,10 @@ namespace Dcrypt\Tests; +use Dcrypt\Exceptions\InvalidChecksumException; +use Dcrypt\OpensslKey; use Dcrypt\OpensslStatic; +use Exception; class OpensslStaticTest extends \PHPUnit\Framework\TestCase { @@ -16,9 +19,8 @@ public function testVectorsAlgos() foreach ($json->algos as $algo => $data) { try { - $plaintext = \Dcrypt\OpensslStatic::decrypt(base64_decode($data), $json->key, 'aes-256-gcm', $algo); - } catch (\Exception | \Error $e) { - throw new \Exception("Failure in [$algo]: " . $e->getMessage()); + $plaintext = OpensslStatic::decrypt(base64_decode($data), $json->key, 'aes-256-gcm', $algo); + } catch (Exception | \Error $e) { } $this->assertEquals('a secret', $plaintext); @@ -33,9 +35,8 @@ public function testVectorsCiphers() foreach ($json->ciphers as $cipher => $data) { try { - $plaintext = \Dcrypt\OpensslStatic::decrypt(base64_decode($data), $json->key, $cipher, 'sha3-256'); - } catch (\Exception | \Error $e) { - + $plaintext = OpensslStatic::decrypt(base64_decode($data), $json->key, $cipher, 'sha3-256'); + } catch (Exception | \Error $e) { } $this->assertEquals('a secret', $plaintext); @@ -44,13 +45,13 @@ public function testVectorsCiphers() public function testBadCipherException() { - $key = \Dcrypt\OpensslKey::create(); + $key = OpensslKey::create(); $pass = false; try { OpensslStatic::encrypt('a secret', $key, 'lol this cipher doesnt exist', 'sha3-256'); - } catch (\Exception $e) { + } catch (Exception $e) { $pass = true; } @@ -59,13 +60,13 @@ public function testBadCipherException() public function testBadAlgoException() { - $key = \Dcrypt\OpensslKey::create(); + $key = OpensslKey::create(); $pass = false; try { OpensslStatic::encrypt('a secret', $key, 'aes-256-gcm', 'lol this algo doesnt exist'); - } catch (\Exception $e) { + } catch (Exception $e) { $pass = true; } @@ -74,11 +75,11 @@ public function testBadAlgoException() public function testCrossDecryptFails() { - $key = \Dcrypt\OpensslKey::create(); + $key = OpensslKey::create(); - $this->expectException(\Dcrypt\Exceptions\InvalidChecksumException::class); + $this->expectException(InvalidChecksumException::class); - $e = \Dcrypt\OpensslStatic::encrypt('AAAA', $key, 'aes-256-gcm', 'sha256'); - $d = \Dcrypt\OpensslStatic::decrypt($e, $key, 'aes-256-ctr', 'sha256'); + $e = OpensslStatic::encrypt('AAAA', $key, 'aes-256-gcm', 'sha256'); + $d = OpensslStatic::decrypt($e, $key, 'aes-256-ctr', 'sha256'); } } diff --git a/examples/vectors.php b/tests/vectors.php similarity index 90% rename from examples/vectors.php rename to tests/vectors.php index a50e1b03..4376dc5d 100644 --- a/examples/vectors.php +++ b/tests/vectors.php @@ -10,7 +10,7 @@ require __DIR__ . '/../vendor/autoload.php'; -$vectors = __DIR__ . '/../tests/.vectors.json'; +$vectors = __DIR__ . '.vectors.json'; if (file_exists($vectors)) { $key = json_decode(file_get_contents($vectors))->key; @@ -56,6 +56,6 @@ $out['otp'][$mult] = \base64_encode(\Dcrypt\OneTimePad::crypt(str_repeat('A', $mult), $key)); } -file_put_contents(__DIR__ . '/../tests/.vectors.json', \json_encode($out, JSON_PRETTY_PRINT)); +file_put_contents($vectors, \json_encode($out, JSON_PRETTY_PRINT)); var_dump($out);