diff --git a/.travis.yml b/.travis.yml index b60908f..66d4d68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: php php: - - 5.5 - - 5.4 - - 5.3 + - 7.1 + - 7.0 + - 5.6 # Notifications notifications: diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4a87575 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,120 @@ +# Changelog + +## 2.0.0 + +### New Password Hasher + +Version `2.0.0` comes with a new password hasher component: `AngryBytes\Hash\Hasher\Password`. +This hasher is intended to be used for hashing of passwords (and other secure tokens). + +The hasher utilises PHP's native cryptographically strong password hashing functions: +`password_hash()` and `password_verify()`, see [Password Hashing](http://php.net/manual/en/book.password.php). + +The password hasher has been setup to use PHP's default cost and algorithm. +The default cost can however be overwritten by providing a cost to the the constructor, like so: + +```php +// Create a password hasher with n cost factor of 15 instead of the default (10). +$passwordHasher = new \AngryBytes\Hash\Hasher\Password(15); +``` + +#### Password Rehashing +The password hasher offers a method to check if an existing hash needs to be **rehashed**. +For example, this can be the case when the cost and/or algorithm of the password +hasher has changed. If this is the case, you **should** rehash the password +and update the stored hash with the rehashed value. + +**Example** + +In this example, we check whether an existing password is outdated and should be rehashed. + +Note: in order to rehash the password, you will need access to its original plaintext value. +Therefore, it's probably a best practice to check for and update a stale hash +during login procedure, where the plaintext password is available after a login form +submit. + +```php +// Create a password hasher +$hasher = new \AngryBytes\Hash\Hash( + new \AngryBytes\Hash\Hasher\Password +); + +// Plaintext password received via form submit. +$password = '...'; + +// Persisted password hash for the User +$userPasswordHash = '...'; + +// Verify the password against the hash +if ($hasher->verify($password, $userPasswordHash)) { + + // Check if the hash needs to be rehashed? + if ($hasher->needsRehash($userPasswordHash)) { + // Rehash password and update the user. + $user->hash = $hasher->hash($password); + $user->save(); + } +} +``` + +### Refactored "AngryBytes\Hash\HasherInterface" + +Added new verification method `AngryBytes\Hash\HasherInterface::verify()` hash +verification. This method accepts the following three arguments: + +* `$data` - The data that needs to be hashed. +* `$hash` - The hash that needs to match the hashed value of `$data`. +* `$options` (optional) - An array with addition hasher options. What these options are depends on the active hasher. + +### Refactored AngryBytes\Hash\Hash + +`AngryBytes\Hash\Hash::construct()` second argument (`$salt`) has become optional +to accommodate for hashers that handle their own salt, like `AngryBytes\Hash\Hasher\Password`. + +`AngryBytes\Hash\Hash::hash()` and `AngryBytes\Hash\Hash::shortHash()` no longer accept any number of arguments but +only following two: + +* `$data` - The data that needs to be hashed. This data can be of any type, all non-scalar types will be + serialized before hashing. +* `$options` (optional) - An array with options that will be passed to the hasher. What these options are depends + on the active hasher. + +`AngryBytes\Hash\Hash::verify()` is a new method that's available to validate a hash in a time-safe manner. +The method accepts the following arguments: + +* `$data` - The data that needs to be hashed. This data can be of any type, all non-scalar types will be + serialized before hashing. +* `$hash` - The hash that needs to match the hashed value of `$data`. +* `$options` (optional) - An array with options that will be passed to the hasher. What these options are depends + on the active hasher. + +`AngryBytes\Hash\Hash::matchesShortHash()` is replaced by `AngryBytes\Hash\Hash::verifyShortHash()` this methods +accepts three arguments (`$data`, `$hash` and `$options`) just like `AngryBytes\Hash\Hash::verify()`. + +### Minor Changes + +* Scalar values passed to `hash()` and `shortHash()` are no longer serialized. +* `AngryBytes\Hash::compare()` now uses PHP native (timing attack safe) `hash_equals()` function. +* Fixed namespace issues for `AngryBytes\Hash\HMAC`. +* `AngryBytes\Hash\Hash` now implements a `__call()` method that dynamically passes + methods to the active hasher. This allows you to perform calls such as `AngryBytes\Hash::hash($string, ['cost' => 15]);` + without having to call `AngryBytes\Hash::getHasher()` first. + +### Upgrading + +Please refer to the [upgrade notes](UPGRADING.md). + +### Deprecated & Removed Components + +* `AngryBytes\Hash\RandomString` has been removed. Better open-source random string generation + libraries are available to do this. + +## 1.0.2 +Valid Blowfish salts (22 char long composed of ./A-Za-z0-9 characters only) are now used as the salt as-is instead +of md5-ed and substring-ed. + +## 1.0.1 +Adding travis build status and scrutinizer code qual. img. to readme + +## 1.0.0 +Initial release diff --git a/README.md b/README.md index b8e6a11..1fad1f1 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,43 @@ # AngryBytes Hash +[![Author](http://img.shields.io/badge/author-@angrybytes-blue.svg?style=flat-square)](https://twitter.com/angrybytes) +[![Software License](https://img.shields.io/badge/license-proprietary-brightgreen.svg?style=flat-square)](LICENSE.md) [![Build Status](https://travis-ci.org/AngryBytes/hash.svg?branch=master)](https://travis-ci.org/AngryBytes/hash) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/AngryBytes/hash/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/AngryBytes/hash/?branch=master) A simple PHP library that simplifies cryptographical hashing. It provides an object oriented interface to a variety of hashing methods. -## Contents +## Requirements + +* PHP `5.6.0` or `PHP 7.0` (recommended) + +## Installation + +Installation is done via Composer: `composer require angrybytes/hash`. + +## Components ### Hash -`AngryBytes\Hash\Hash` is the main hasher class. It uses one of the `Hashers` to do the work. +`AngryBytes\Hash\Hash` is the main hasher class and acts as a helper wrapper +around hashers (i.e. `AngryBytes\Hash\HasherInterface` implementations). + +Some of the main features of this component: -Available hashers are: +* Hash strings and/or passwords. +* Create short hashes (e.g. used for identification). +* Compare strings/hashes using a time-safe method. +* Verify a string against a hash using the configured hasher. + +### Hashers + +This library comes with a set of hashers to be utilised by this hash component (or +to be used on their own): * `AngryBytes\Hash\Hasher\BlowFish` * `AngryBytes\Hash\Hasher\MD5` - -In addition this class can compare strings/hashes using a time-safe method. + * `AngryBytes\Hash\Hasher\Password` ### HMAC @@ -25,8 +45,10 @@ In addition this class can compare strings/hashes using a time-safe method. [HMAC's](http://en.wikipedia.org/wiki/Hash-based_message_authentication_code) for string messages. -### Random Strings +## Contributing + +Before contributing to this project, please read the [contributing notes](CONTRIBUTING.md). + +## License -Also included is a basic random string generator in -`AngryBytes\Hash\RandomString`. It targets Unix systems with `/dev/urandom` -available for now. +Please refer to the [license file](LICENSE.md). diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 0000000..b87d0c6 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,74 @@ +# Upgrade Notes + +This document lists important upgrade nodes and BC breaks per version. + +## 2.0.0 + +### Update Hashing Values + +If you are hashing multiple values you will need to change the way their are passed +to the hasher. Instead of passing each variable separately you will need to pass +them as an array. Like so: + +```php + +// Create a new Password Hasher +$hasher = new \AngryBytes\Hash\Hash( + new \AngryBytes\Hash\Hasher\Password +); + +// Old +$hash = $hasher->hash('foo', 'bar', ['foo', 'bar']); + +// New +$hash = $hasher->hash([ + 'foo', 'bar', ['foo', 'bar'] +]); +``` + +The same principle applies to `Hash::shortHash()`. + +### Update Hash Validation + +In the previous version hash validation would be done by creating a hash and comparing +it to the existing hash. Now, you can simple pass the value(s) to hash and the +existing hash to methods: `verify()` or `verfiyShortHash()`. + +```php +// Create a new Password Hasher +$hasher = new \AngryBytes\Hash\Hash( + new \AngryBytes\Hash\Hasher\Password +); + +// The origin and hashed values +$valueToHash = '...' +$existingHash = '...'; + +// Old +$isValid = \AngryBytes\Hash\Hash::compare( + $hasher->hash($valueToHash), + $existingHash +); + +// New +$isValid = $hasher->verify($valueToHash, $existingHash) +``` + +And for short hash comparison: + +```php +// Create a new Password Hasher +$hasher = new \AngryBytes\Hash\Hash( + new \AngryBytes\Hash\Hasher\Password +); + +// Old +if (Hash::matchShortHash($hasher->shortHash($value), $existingShortHash)) { + // Hash is valid +} + +// New +if ($hasher->verifyShortHash($value, $existingShortHash)) { + // Hash is valid +} +``` diff --git a/composer.json b/composer.json index bd8dacc..c398d06 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,10 @@ "AngryBytes\\Hash": "src" } }, + "require": { + "php": "~5.6 || ~7.0" + }, "require-dev": { - "phpunit/phpunit": "3.7.x" + "phpunit/phpunit" : "~5.4.0" } } diff --git a/composer.lock b/composer.lock index 3615367..502a540 100644 --- a/composer.lock +++ b/composer.lock @@ -1,55 +1,361 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" - ], - "hash": "0469325c580a878c65a7e5f2ab661967", - "packages": [ - + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" ], + "hash": "b706f018cb6669669b76a3c5099a0a14", + "content-hash": "e8a906c6aca8a7ab6b52815b181a359a", + "packages": [], "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "myclabs/deep-copy", + "version": "1.5.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "ea74994a3dc7f8d2f65a06009348f2d63c81e61f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/ea74994a3dc7f8d2f65a06009348f2d63c81e61f", + "reference": "ea74994a3dc7f8d2f65a06009348f2d63c81e61f", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "doctrine/collections": "1.*", + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "homepage": "https://github.com/myclabs/DeepCopy", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2016-09-16 13:37:59" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2015-12-27 11:43:31" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.2.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2016-09-30 07:12:33" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443", + "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2016-06-10 07:14:17" + }, + { + "name": "phpspec/prophecy", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1", + "sebastian/recursion-context": "^1.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2016-06-07 08:13:47" + }, { "name": "phpunit/php-code-coverage", - "version": "1.2.12", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "0e9958c459d675fb497d8dc5001c91d335734e48" + "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e9958c459d675fb497d8dc5001c91d335734e48", - "reference": "0e9958c459d675fb497d8dc5001c91d335734e48", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3", + "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": ">=1.3.0@stable", - "phpunit/php-text-template": ">=1.1.1@stable", - "phpunit/php-token-stream": ">=1.1.3@stable" + "php": "^5.6 || ^7.0", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "^1.4.2", + "sebastian/code-unit-reverse-lookup": "~1.0", + "sebastian/environment": "^1.3.2 || ^2.0", + "sebastian/version": "~1.0|~2.0" }, "require-dev": { - "phpunit/phpunit": "3.7.*@dev" + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "^5.4" }, "suggest": { "ext-dom": "*", - "ext-xdebug": ">=2.0.5" + "ext-xdebug": ">=2.4.0", + "ext-xmlwriter": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { "classmap": [ - "PHP/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], @@ -67,35 +373,37 @@ "testing", "xunit" ], - "time": "2013-07-06 06:26:16" + "time": "2016-07-26 14:39:29" }, { "name": "phpunit/php-file-iterator", - "version": "1.3.3", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "16a78140ed2fc01b945cfa539665fadc6a038029" + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/16a78140ed2fc01b945cfa539665fadc6a038029", - "reference": "16a78140ed2fc01b945cfa539665fadc6a038029", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, "autoload": { "classmap": [ - "File/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], @@ -107,25 +415,25 @@ } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "http://www.phpunit.de/", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ "filesystem", "iterator" ], - "time": "2012-10-11 11:44:38" + "time": "2015-06-21 13:08:43" }, { "name": "phpunit/php-text-template", - "version": "1.1.4", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23" + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5180896f51c5b3648ac946b05f9ec02be78a0b23", - "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "shasum": "" }, "require": { @@ -134,20 +442,17 @@ "type": "library", "autoload": { "classmap": [ - "Text/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -156,35 +461,35 @@ "keywords": [ "template" ], - "time": "2012-10-31 18:15:28" + "time": "2015-06-21 13:50:34" }, { "name": "phpunit/php-timer", - "version": "1.0.5", + "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "require-dev": { + "phpunit/phpunit": "~4|~5" + }, "type": "library", "autoload": { "classmap": [ - "PHP/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], @@ -200,49 +505,48 @@ "keywords": [ "timer" ], - "time": "2013-08-02 07:42:54" + "time": "2016-05-12 18:03:57" }, { "name": "phpunit/php-token-stream", - "version": "1.2.0", + "version": "1.4.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "31babf400e5b5868573bf49a000a3519d3978233" + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/31babf400e5b5868573bf49a000a3519d3978233", - "reference": "31babf400e5b5868573bf49a000a3519d3978233", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=5.3.3" }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.4-dev" } }, "autoload": { "classmap": [ - "PHP/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], "description": "Wrapper around PHP's tokenizer extension.", @@ -250,63 +554,67 @@ "keywords": [ "tokenizer" ], - "time": "2013-08-04 05:57:48" + "time": "2015-09-15 10:49:45" }, { "name": "phpunit/phpunit", - "version": "3.7.24", + "version": "5.4.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "af7b77ccb5c64458bdfca95665d29558d1df7d08" + "reference": "3132365e1430c091f208e120b8845d39c25f20e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/af7b77ccb5c64458bdfca95665d29558d1df7d08", - "reference": "af7b77ccb5c64458bdfca95665d29558d1df7d08", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3132365e1430c091f208e120b8845d39c25f20e6", + "reference": "3132365e1430c091f208e120b8845d39c25f20e6", "shasum": "" }, "require": { "ext-dom": "*", + "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", - "php": ">=5.3.3", - "phpunit/php-code-coverage": "~1.2.1", - "phpunit/php-file-iterator": ">=1.3.1", - "phpunit/php-text-template": ">=1.1.1", - "phpunit/php-timer": ">=1.0.4", - "phpunit/phpunit-mock-objects": "~1.2.0", - "symfony/yaml": "~2.0" + "myclabs/deep-copy": "~1.3", + "php": "^5.6 || ^7.0", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "^4.0.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "^3.2", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "^1.3 || ^2.0", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/object-enumerator": "~1.0", + "sebastian/resource-operations": "~1.0", + "sebastian/version": "~1.0|~2.0", + "symfony/yaml": "~2.1|~3.0" }, - "require-dev": { - "pear-pear/pear": "1.9.4" + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2" }, "suggest": { - "ext-json": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "phpunit/php-invoker": ">=1.1.0,<1.2.0" + "phpunit/php-invoker": "~1.1" }, "bin": [ - "composer/bin/phpunit" + "phpunit" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.7.x-dev" + "dev-master": "5.4.x-dev" } }, "autoload": { "classmap": [ - "PHPUnit/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], "license": [ "BSD-3-Clause" ], @@ -318,45 +626,55 @@ } ], "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", + "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], - "time": "2013-08-09 06:58:24" + "time": "2016-07-26 14:48:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "1.2.3", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" + "reference": "7462c19bdb9814f6e6bdeb5cad3eb3ce72c6e0da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", - "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/7462c19bdb9814f6e6bdeb5cad3eb3ce72c6e0da", + "reference": "7462c19bdb9814f6e6bdeb5cad3eb3ce72c6e0da", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-text-template": ">=1.1.1@stable" + "doctrine/instantiator": "^1.0.2", + "php": "^5.6 || ^7.0", + "phpunit/php-text-template": "^1.2", + "sebastian/exporter": "^1.2" + }, + "conflict": { + "phpunit/phpunit": "<5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.4" }, "suggest": { "ext-soap": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev" + } + }, "autoload": { "classmap": [ - "PHPUnit/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], @@ -373,67 +691,628 @@ "mock", "xunit" ], - "time": "2013-01-13 10:24:48" + "time": "2016-09-27 03:17:40" }, { - "name": "symfony/yaml", - "version": "v2.3.4", - "target-dir": "Symfony/Component/Yaml", + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "5a279f1b5f5e1045a6c432354d9ea727ff3a9847" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/5a279f1b5f5e1045a6c432354d9ea727ff3a9847", - "reference": "5a279f1b5f5e1045a6c432354d9ea727ff3a9847", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", + "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.6" + }, + "require-dev": { + "phpunit/phpunit": "~5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2013-08-24 15:26:22" - } - ], - "aliases": [ - - ], - "minimum-stability": "stable", - "stability-flags": [ - - ], - "platform": [ - - ], - "platform-dev": [ - - ] + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2016-02-13 06:45:14" + }, + { + "name": "sebastian/comparator", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-07-26 15:48:44" + }, + { + "name": "sebastian/diff", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-12-08 07:14:41" + }, + { + "name": "sebastian/environment", + "version": "1.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2016-08-18 05:49:44" + }, + { + "name": "sebastian/exporter", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2016-06-17 09:04:28" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12 03:26:01" + }, + { + "name": "sebastian/object-enumerator", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "d4ca2fb70344987502567bc50081c03e6192fb26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d4ca2fb70344987502567bc50081c03e6192fb26", + "reference": "d4ca2fb70344987502567bc50081c03e6192fb26", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2016-01-28 13:25:10" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2015-11-11 19:50:13" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2015-07-28 20:34:47" + }, + { + "name": "sebastian/version", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5", + "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-02-04 12:56:52" + }, + { + "name": "symfony/yaml", + "version": "v3.1.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "f291ed25eb1435bddbe8a96caaef16469c2a092d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f291ed25eb1435bddbe8a96caaef16469c2a092d", + "reference": "f291ed25eb1435bddbe8a96caaef16469c2a092d", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2016-09-02 02:12:52" + }, + { + "name": "webmozart/assert", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "bb2d123231c095735130cc8f6d31385a44c7b308" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/bb2d123231c095735130cc8f6d31385a44c7b308", + "reference": "bb2d123231c095735130cc8f6d31385a44c7b308", + "shasum": "" + }, + "require": { + "php": "^5.3.3|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-08-09 15:02:57" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "~5.6 || ~7.0" + }, + "platform-dev": [] } diff --git a/src/AngryBytes/Hash/HMAC.php b/src/AngryBytes/Hash/HMAC.php index e58afc8..3e38d03 100644 --- a/src/AngryBytes/Hash/HMAC.php +++ b/src/AngryBytes/Hash/HMAC.php @@ -4,16 +4,14 @@ * * @category AngryBytes * @package Hash - * @copyright Copyright (c) 2010 Angry Bytes BV (http://www.angrybytes.com) + * @copyright Copyright (c) 2007-2016 Angry Bytes BV (http://www.angrybytes.com) */ -use Angrybytes\Hash\Hash; +namespace AngryBytes\Hash; use \InvalidArgumentException; /** - * HMAC - * * HMAC creator * * This class will generate hashes to be used as HMAC @@ -33,8 +31,7 @@ class HMAC /** * Constructor * - * @param string $algorithm - * @return void + * @param string $algorithm **/ public function __construct($algorithm) { @@ -44,43 +41,14 @@ public function __construct($algorithm) /** * Does this platform support an algorithm? * + * @param string $algorithm * @return bool **/ - public static function platformSupports($algorithm) + public static function platformSupportsAlgorithm($algorithm) { return in_array($algorithm, hash_algos()); } - /** - * Get the algorithm to use - * - * @return string - */ - public function getAlgorithm() - { - return $this->algorithm; - } - - /** - * Set the algorithm to use - * - * @param string $algorithm - * @return HMAC - */ - public function setAlgorithm($algorithm) - { - // Sanity check - if (!self::platformSupports($algorithm)) { - throw new InvalidArgumentException(sprintf( - '"%s" is not a supported hash algorithm on this platform' - )); - } - - $this->algorithm = $algorithm; - - return $this; - } - /** * Create an HMAC * @@ -88,24 +56,14 @@ public function setAlgorithm($algorithm) * strings. All input will be concatenated before hashing. * * @param string $sharedSecret - * @param mixed $what1 - * @param mixed $what2 - * ... - * @param mixed $whatN + * @param array $args * @return string - **/ - public function hmac($sharedSecret) + */ + public function hmac($sharedSecret, ...$args) { - // Get data as a string of passed args - $args = func_get_args(); - - // Shift the shared secret off - array_shift($args); - // Get the data concatenated $data = ''; foreach ($args as $index => $arg) { - // Sanity check if (!is_string($arg)) { throw new InvalidArgumentException(sprintf( @@ -118,7 +76,7 @@ public function hmac($sharedSecret) } return hash_hmac( - $this->getAlgorithm(), + $this->algorithm, $data, $sharedSecret ); @@ -134,10 +92,31 @@ public function hmac($sharedSecret) **/ public function validHmac($message, $hmac, $sharedSecret) { - // The HMAC as it should be for our shared secret - $shouldHave = $this->hmac($sharedSecret, $message); + // Compare HMAC with received message + return Hash::compare( + $hmac, + // The HMAC as it should be for our shared secret + $this->hmac($sharedSecret, $message) + ); + } - // Compare - return Hash::compare($hmac, $shouldHave); + /** + * Set the algorithm to use + * + * @param string $algorithm + * @return HMAC + */ + protected function setAlgorithm($algorithm) + { + // Sanity check + if (!self::platformSupportsAlgorithm($algorithm)) { + throw new InvalidArgumentException(sprintf( + '"%s" is not a supported hash algorithm on this platform' + )); + } + + $this->algorithm = $algorithm; + + return $this; } } diff --git a/src/AngryBytes/Hash/Hash.php b/src/AngryBytes/Hash/Hash.php index 408b45d..99f1fb4 100644 --- a/src/AngryBytes/Hash/Hash.php +++ b/src/AngryBytes/Hash/Hash.php @@ -1,25 +1,24 @@ setHasher($hasher) - ->setSalt($salt); + $this->hasher = $hasher; + + $this->setSalt($salt); } /** - * Get the hasher to use for the actual hashing + * Dynamically pass methods to the active hasher * - * @return HasherInterface + * @param string $method + * @param array $parameters */ - public function getHasher() + public function __call($method, $parameters) { - return $this->hasher; + return $this->hasher->$method(...$parameters); } /** - * Set the hasher to use for the actual hashing + * Get the hasher to use for the actual hashing * - * @param HasherInterface $hasher - * @return Hash + * @return HasherInterface */ - public function setHasher(HasherInterface $hasher) + public function getHasher() { - $this->hasher = $hasher; - - return $this; + return $this->hasher; } /** - * Get the salt + * Generate a hash * + * @param mixed $data The data to hash. This can either be a scalar value or a serializable value. + * @param mixed[] $options Additional hasher options (the actual options depend on the registered Hasher) * @return string - */ - public function getSalt() + **/ + public function hash($data, array $options = []) { - return $this->salt; + return $this->hasher->hash( + self::getDataString($data), + $this->parseHashOptions($options) + ); } /** - * Set the salt + * Verify if the data matches the hash * - * @param string $salt - * @return Hash + * @param mixed $data The data to verify against the hash string. This can either be a scalar value or a serializable value. + * @param string $hash + * @param mixed[] $options + * @return bool */ - public function setSalt($salt) - { - // Make sure it's of sufficient length - if (strlen($salt) < 20) { - throw new InvalidArgumentException(sprintf( - 'Provided salt "%s" is not long enough. A minimum length of 20 characters is required', - $salt - )); - } - - $this->salt = $salt; - - return $this; - } - - /** - * Generate a hash - * - * Will accept any number of (serializable) variables - * - * @param mixed $what1 - * @param mixed $what2 - * ... - * @param mixed $whatN - * @return string - **/ - public function hash() + public function verify($data, $hash, array $options = []) { - return $this->getHasher()->hash( - call_user_func_array( - array($this, 'getDataString'), - func_get_args() - ), - $this->getSalt() + return $this->hasher->verify( + self::getDataString($data), + $hash, + $this->parseHashOptions($options) ); } /** * Hash something into a short hash * - * This is simply a shortened version of hash(), returning a 7 character - * hash, which should be good enough for identification purposes. It is - * however *not* strong enough for cryptographical uses or password - * storage. Use only to create a fast, short string to compare or identify. + * This is simply a shortened version of hash(), returning a 7 character hash, which should be good + * enough for identification purposes. Therefore it MUST NOT be used for cryptographic purposes or + * password storage but only to create a fast, short string to compare or identify. + * + * It is RECOMMENDED to only use this method with the Hasher\MD5 hasher as hashes + * created by bcrypt/crypt() have a common beginning. * * @see Hash::hash() * - * @param mixed $what1 - * @param mixed $what2 - * ... - * @param mixed $whatN + * @param string $data + * @param mixed[] $options * @return string - **/ - public function shortHash() + */ + public function shortHash($data, array $options = []) { - $fullHash = call_user_func_array( - array($this, 'hash'), - func_get_args() - ); - - return substr($fullHash, 0, 7); + return substr($this->hash($data, $options), 0, 7); } /** - * Does something match a short hash? - * - * First argument to this function is the short hash as it *should* be, all other arguments will - * be passed to shortHash() + * Verify if the data matches the shortHash * * @see Hash::shortHash() * - * @param mixed $what - * @param string $hash + * @param mixed $data + * @param string $shortHash + * @param mixed[] $options * @return bool **/ - public function matchesShortHash() + public function verifyShortHash($data, $shortHash, array $options = []) { - // Full args to method - $args = func_get_args(); - - // Remove compareTo from the beginning - $compareTo = array_shift($args); - - // Create short hash to compare to - $shortHash = call_user_func_array( - array($this, 'shortHash'), - $args + return self::compare( + $this->shortHash($data, $options), + $shortHash ); - - // Compare and return - return self::compare($compareTo, $shortHash); } /** * Compare two hashes * - * This method is time-safe, i.e. it will take the same amount of time to - * execute for all inputs. This is critical to avoid timing attacks. - * - * NOTE: - * - * **This method only works on ASCII strings.** + * Uses the time-save `hash_equals()` function to compare 2 hashes. * * @param string $hashA * @param string $hashB @@ -201,38 +155,66 @@ public function matchesShortHash() **/ public static function compare($hashA, $hashB) { - // Compare length - if (strlen($hashA) !== strlen($hashB)) { - return false; + return hash_equals($hashA, $hashB); + } + + /** + * Set the salt + * + * @param string|null $salt + * @return Hash + */ + protected function setSalt($salt) + { + if (is_string($salt) && (strlen($salt) < 20 || strlen($salt) > CRYPT_SALT_LENGTH)) { + // Make sure it's of sufficient length + throw new InvalidArgumentException(sprintf( + 'Provided salt "%s" does not match the length requirements. A length between 20 en %d characters is required.', + $salt, + CRYPT_SALT_LENGTH + )); } - // bitwise OR total for all characters - $result = 0; + $this->salt = $salt; - for ($charIndex = 0; $charIndex < strlen($hashA); $charIndex++) { - // XOR the value of the ASCII characters at $charIndex - // This value is then OR-ed into the total - $result |= ord($hashA[$charIndex]) ^ ord($hashB[$charIndex]); + return $this; + } + + /** + * Get the data as a string + * + * Will serialize non-scalar values + * + * @param mixed $data + * @return string + */ + private static function getDataString($data) + { + if (is_scalar($data)) { + return (string) $data; } - // If the result is anything but 0 there was a differing character at - // one or more of the positions - return $result === 0; + return serialize($data); } /** - * Get the passed arguments as a string + * Merge the default and provided hash options * - * Accepts anything serializable + * Automatically sets the salt as an option when set in this + * component. * - * @param mixed $what1 - * @param mixed $what2 - * ... - * @param mixed $whatN - * @return string - **/ - private function getDataString() + * @param mixed[] $options + * @return mixed[] + */ + private function parseHashOptions(array $options = []) { - return serialize(func_get_args()); + $defaultOptions = []; + + // Pass the salt if set + if (!is_null($this->salt)) { + $defaultOptions['salt'] = $this->salt; + } + + return array_merge($defaultOptions, $options); } } diff --git a/src/AngryBytes/Hash/Hasher/Blowfish.php b/src/AngryBytes/Hash/Hasher/Blowfish.php index 0759434..9f9c683 100644 --- a/src/AngryBytes/Hash/Hasher/Blowfish.php +++ b/src/AngryBytes/Hash/Hasher/Blowfish.php @@ -5,28 +5,26 @@ * @category AngryBytes * @package Hash * @subpackage Hasher - * @copyright Copyright (c) 2010 Angry Bytes BV (http://www.angrybytes.com) + * @copyright Copyright (c) 2007-2016 Angry Bytes BV (http://www.angrybytes.com) */ namespace AngryBytes\Hash\Hasher; +use AngryBytes\Hash\Hash; use AngryBytes\Hash\HasherInterface; use \RuntimeException; use \InvalidArgumentException; /** - * Blowfish + * Blowfish Hasher * - * Blowfish hasher - * - * Relies on bcrypt/crypt() for the heavy lifting + * Generate and verify Blowfish bcrypt/crypt() hashes. * + * @see AngryBytes\Hasher\Password For a password hasher * @category AngryBytes * @package Hash * @subpackage Hasher - * - * @hootie */ class Blowfish implements HasherInterface { @@ -40,17 +38,24 @@ class Blowfish implements HasherInterface private $workFactor = 15; /** + * Construct + * * Detect Blowfish support * - * @throws RuntimeException - **/ - public function __construct() + * @throws \RuntimeException + * @param null|int $workFactor Override workfactor + */ + public function __construct($workFactor = null) { if (!defined("CRYPT_BLOWFISH") || CRYPT_BLOWFISH !== 1) { throw new RuntimeException( 'Blowfish hashing not available on this installation' ); } + + if (is_int($workFactor)) { + $this->setWorkFactor($workFactor); + } } /** @@ -82,15 +87,26 @@ public function setWorkFactor($workFactor) } /** - * Hash password and salt + * {@inheritDoc} + */ + public function hash($string, array $options = []) + { + $salt = isset($options['salt']) ? $this->bcryptSalt($options['salt']) : null; + + return crypt($string, $salt); + } + + /** + * {@inheritDoc} * - * @param string $data - * @param string $salt - * @return string - **/ - public function hash($data, $salt) + * @see Hash::compare() + */ + public function verify($string, $hash, array $options = []) { - return crypt($data, $this->bcryptSalt($salt)); + return Hash::compare( + $this->hash($string, $options), + $hash + ); } /** @@ -134,4 +150,3 @@ private static function getSaltSubstr($salt) ); } } - diff --git a/src/AngryBytes/Hash/Hasher/MD5.php b/src/AngryBytes/Hash/Hasher/MD5.php index 55a396f..39119ef 100644 --- a/src/AngryBytes/Hash/Hasher/MD5.php +++ b/src/AngryBytes/Hash/Hasher/MD5.php @@ -10,14 +10,20 @@ namespace AngryBytes\Hash\Hasher; +use AngryBytes\Hash\Hash; use AngryBytes\Hash\HasherInterface; /** - * MD5 + * MD5 Hasher * - * MD5 hasher. + * Generate and verify MD5 hashes. * - * You probably shouldn't use this for passwords + * NOTE: + * + * This hasher MUST NOT be used for password storage. It is RECOMMENDED + * to use the Hasher\Password for this purpose + * + * @see AngryBytes\Hasher\Password For a password hasher * * @category AngryBytes * @package Hash @@ -26,14 +32,25 @@ class MD5 implements HasherInterface { /** - * Hash a value using md5 + * {@inheritDoc} + */ + public function hash($string, array $options = []) + { + $salt = isset($options['salt']) ? $options['salt'] : ''; + + return md5($string . '-' . $salt); + } + + /** + * {@inheritDoc} * - * @param string $data - * @param string $salt - * @return string + * @see Hash::compare() */ - public function hash($data, $salt) + public function verify($string, $hash, array $options = []) { - return md5($data . '-' . $salt); + return Hash::compare( + $this->hash($string, $options), + $hash + ); } } diff --git a/src/AngryBytes/Hash/Hasher/Password.php b/src/AngryBytes/Hash/Hasher/Password.php new file mode 100644 index 0000000..a3a49d7 --- /dev/null +++ b/src/AngryBytes/Hash/Hasher/Password.php @@ -0,0 +1,138 @@ +setCost($cost); + } + + /** + * {@inheritDoc} + * + * Supported options in $options array: + * - 'salt': Override the salt generated by password_hash() (discouraged) + * - 'cost': Override the default cost (not advised) + * + * @throws RuntimeException If the hashing fails + */ + public function hash($data, array $options = []) + { + $hash = password_hash($data, PASSWORD_DEFAULT, $this->parsePasswordOptions($options)); + if ($hash === false) { + throw new RuntimeException('Failed to hash password'); + } + + return $hash; + } + + /** + * {@inheritDoc} + */ + public function verify($string, $hash, array $options = []) + { + return password_verify($string, $hash); + } + + /** + * Determine if the password needs to be rehashed based on the hash options + * + * If true, the password should be rehashed after verification. + * + * @param string $hash + * @param array $options Password options, @see hash() + * @return bool + */ + public function needsRehash($hash, array $options = []) + { + return password_needs_rehash($hash, PASSWORD_DEFAULT, $this->parsePasswordOptions($options)); + } + + /** + * Get info for the given hash + * + * @see password_get_info() + * @param string $hash + * @return mixed[] + */ + public function getInfo($hash) + { + return password_get_info($hash); + } + + /** + * Set cost + * + * @throws InvalidArgumentException if the cost is too high or low + * @param int|null $cost + */ + public function setCost($cost) + { + if (is_null($cost)) { + $this->cost = $cost; + + return; + } + + if ($cost < 4 || $cost > 31) { + throw new InvalidArgumentException(sprintf( + 'Cost value "%d" needs to be greater than 3 and smaller than 32', (int) $cost + )); + } + + $this->cost = (int) $cost; + } + + /** + * Parse password options for hash methods + * + * @param array $options + * @return array + */ + private function parsePasswordOptions(array $options) + { + // Parse options + if (!isset($options['cost']) && is_int($this->cost)) { + $options['cost'] = $this->cost; + } + + return $options; + } +} diff --git a/src/AngryBytes/Hash/HasherInterface.php b/src/AngryBytes/Hash/HasherInterface.php index 0c903bf..638d59d 100644 --- a/src/AngryBytes/Hash/HasherInterface.php +++ b/src/AngryBytes/Hash/HasherInterface.php @@ -5,15 +5,13 @@ * @category AngryBytes * @package Hash * @subpackage Hasher - * @copyright Copyright (c) 2010 Angry Bytes BV (http://www.angrybytes.com) + * @copyright Copyright (c) 2007-2016 Angry Bytes BV (http://www.angrybytes.com) */ namespace AngryBytes\Hash; /** - * HasherInterface - * - * Interface for hashers + * Common Contract For Hashers * * @category AngryBytes * @package Hash @@ -24,11 +22,19 @@ interface HasherInterface /** * Hash a data string * - * Implementation is supposed to salt the hashing method using $salt - * - * @param string $data - * @param string $salt + * @param string $string + * @param array $options Additional hasher options * @return string **/ - public function hash($data, $salt); + public function hash($string, array $options = []); + + /** + * Verify is the data string matches the given hash + * + * @param string $string + * @param string $hash + * @param array $options Additional hasher options + * @return bool + */ + public function verify($string, $hash, array $options = []); } diff --git a/src/AngryBytes/Hash/RandomString.php b/src/AngryBytes/Hash/RandomString.php deleted file mode 100644 index 080c81c..0000000 --- a/src/AngryBytes/Hash/RandomString.php +++ /dev/null @@ -1,67 +0,0 @@ -assertEquals( - '$2y$15$aa5c57dda7634fc90a92duWE0jEXBsxhrZrjtIDNJxqSVAgleehYW', + '$2y$15$aa5c57dda7634fc90a92duQSfz3E1u39Z6s63i6l5QpvgJK5tSKri', $hasher->hash('foo') ); $this->assertNotEquals( - '$2y$15$aa5c57dda7634fc90a92duWE0jEXBsxhrZrjtIDNJxqSVAgleehYW', + '$2y$15$aa5c57dda7634fc90a92duQSfz3E1u39Z6s63i6l5QpvgJK5tSKri', $hasher->hash('bar') ); } /** - * Test complex serialized data + * Test complex serialized data hashing * * @return void **/ @@ -62,7 +58,7 @@ public function testSerialized() 12345 ); $this->assertEquals( - '$2y$15$aa5c57dda7634fc90a92duEeKPUIy2mlag3NxVWnd1S.pWl0l6Vkq', + '$2y$15$aa5c57dda7634fc90a92duDv2OoNSn8R.p3.GSoaEZd6/vdiiq9lG', $hasher->hash($data) ); @@ -71,11 +67,53 @@ public function testSerialized() // Should no longer match $this->assertNotEquals( - '$2y$15$aa5c57dda7634fc90a92duEeKPUIy2mlag3NxVWnd1S.pWl0l6Vkq', + '$2y$15$aa5c57dda7634fc90a92duDv2OoNSn8R.p3.GSoaEZd6/vdiiq9lG', $hasher->hash($data) ); } + /** + * Test verification of string hashes + */ + public function testStringVerify() + { + $hasher = $this->createHasher(); + + $this->assertTrue( + $hasher->verify('foo', '$2y$15$aa5c57dda7634fc90a92duQSfz3E1u39Z6s63i6l5QpvgJK5tSKri') + ); + + $this->assertFalse( + $hasher->verify('bar', '$2y$15$aa5c57dda7634fc90a92duQSfz3E1u39Z6s63i6l5QpvgJK5tSKri') + ); + } + + /** + * Test verification of object hashes + */ + public function testObjectVerify() + { + $hasher = $this->createHasher(); + + // Complex data + $data = array( + new \stdClass, + array('foo', 'bar'), + 12345 + ); + + $this->assertTrue( + $hasher->verify($data, '$2y$15$aa5c57dda7634fc90a92duDv2OoNSn8R.p3.GSoaEZd6/vdiiq9lG') + ); + + // Append to data + $data[] = 'foo'; + + $this->assertFalse( + $hasher->verify($data, '$2y$15$aa5c57dda7634fc90a92duDv2OoNSn8R.p3.GSoaEZd6/vdiiq9lG') + ); + } + /** * Test invalid work factor * @@ -103,7 +141,7 @@ public function testWorkFactorTooHigh() } /** - * Test hashing of blowfish + * Test work factor alteration * * @return void **/ @@ -115,13 +153,13 @@ public function testWorkFactor() // Simple string $this->assertEquals( - '$2y$05$aa5c57dda7634fc90a92duqnvmZIAm8fd3YauHfd2Lyt.5Rlz6BsC', + '$2y$05$aa5c57dda7634fc90a92duCIqZ6agXYH9mOnF/It6sfh3MAJAkKXe', $hasher->hash('foo') ); $hasher->getHasher()->setWorkFactor(10); $this->assertEquals( - '$2y$10$aa5c57dda7634fc90a92duaIgY20lEZW.nomcy7J7xN3jNAn5pvge', + '$2y$10$aa5c57dda7634fc90a92duoe.XRVTsrN1oW9P.qnaa.W0BGQ9olPy', $hasher->hash('foo') ); } @@ -138,14 +176,20 @@ public function testSalt() $this->assertEquals( // Pre-generated hash outcome for password 'foo' and given salt '$2y$05$./A1aaaaaaaaaaaaaaaaaOZW9OJaO6Alj4.ZDbOi6Jrbn.bGZfYRK', - $hasher->getHasher()->hash('foo', './A1aaaaaaaaaaaaaaaaaa') + $hasher->getHasher()->hash( + 'foo', + ['salt' => './A1aaaaaaaaaaaaaaaaaa'] + ) ); // Test salt with less invalid characters $this->assertEquals( // Pre-generated hash outcome for password 'foo' and given salt (md5'ed) '$2y$05$ceb20772e0c9d240c75ebugm2AOmnuR5.LsdpDZGAjkE1DupDTPFW', - $hasher->getHasher()->hash('foo', 'salt') + $hasher->getHasher()->hash( + 'foo', + ['salt' => 'salt'] + ) ); } diff --git a/tests/AngryBytes/Hash/Test/HMACTest.php b/tests/AngryBytes/Hash/Test/HMACTest.php new file mode 100644 index 0000000..73bcb10 --- /dev/null +++ b/tests/AngryBytes/Hash/Test/HMACTest.php @@ -0,0 +1,97 @@ +createHasher(); + + $this->assertEquals( + '304692bc0cd231dc337107a7964752858a78140e9696b1ffc2e9a646961ed812f890395480b7c2e8650d55c7d0b0bfe4551b5ce52fae0d73ee5e2c0b0609e164', + $hasher->hmac($this->secret, 'my message') + + ); + + $this->assertNotEquals( + '304692bc0cd231dc337107a7964752858a78140e9696b1ffc2e9a646961ed812f890395480b7c2e8650d55c7d0b0bfe4551b5ce52fae0d73ee5e2c0b0609e164', + $hasher->hmac('foo', 'my message') + ); + + $this->assertNotEquals( + '304692bc0cd231dc337107a7964752858a78140e9696b1ffc2e9a646961ed812f890395480b7c2e8650d55c7d0b0bfe4551b5ce52fae0d73ee5e2c0b0609e164', + $hasher->hmac($this->secret, 'some other message') + ); + } + + /** + * Test validation of HMAC hashes + */ + public function testValidateHmac() + { + $hasher = $this->createHasher(); + + $this->assertTrue( + $hasher->validHmac( + 'my message', + '304692bc0cd231dc337107a7964752858a78140e9696b1ffc2e9a646961ed812f890395480b7c2e8650d55c7d0b0bfe4551b5ce52fae0d73ee5e2c0b0609e164', + $this->secret + ) + ); + + $this->assertFalse( + $hasher->validHmac( + 'my message', + '304692bc0cd231dc337107a7964752858a78140e9696b1ffc2e9a646961ed812f890395480b7c2e8650d55c7d0b0bfe4551b5ce52fae0d73ee5e2c0b0609e164', + 'foo' + ) + ); + + $this->assertFalse( + $hasher->validHmac( + 'some other message', + '304692bc0cd231dc337107a7964752858a78140e9696b1ffc2e9a646961ed812f890395480b7c2e8650d55c7d0b0bfe4551b5ce52fae0d73ee5e2c0b0609e164', + $this->secret + ) + ); + } + + /** + * Create hasher + * + * @return HMAC + */ + private function createHasher() + { + return new HMAC('sha512'); + } +} + diff --git a/tests/AngryBytes/Hash/Test/HashLibTest.php b/tests/AngryBytes/Hash/Test/HashLibTest.php new file mode 100644 index 0000000..14e67a3 --- /dev/null +++ b/tests/AngryBytes/Hash/Test/HashLibTest.php @@ -0,0 +1,63 @@ +createHasher(); $this->assertEquals( - '4dc664001bbbbf88d2b59eeda6855a6b', + 'f149d11c9f6d8e899772b855588722f2', $hasher->hash('foo') ); $this->assertEquals( - 'b5ab8f853032ce68de22035736209e75', + '7b84dc4faca7b93ea519eade24a5f634', $hasher->hash('bar') ); } @@ -59,18 +55,55 @@ public function testHashObject() $obj->foo = 'bar'; $this->assertEquals( - '8ff1610fc9c2607f7d8e9175080f7311', + '2f4162ad4b8774272e452efafd25972f', $hasher->hash($obj) ); $obj->bar = 'foo'; $this->assertEquals( - '458ee16d8b79287fb8cf8700469cc634', + '0d8c867eedd83575b44c62f8caac142b', $hasher->hash($obj) ); } + /** + * Test verification of string hashes + */ + public function testVerifyString() + { + $hasher = $this->createHasher(); + + $this->assertTrue( + $hasher->verify('foo', 'f149d11c9f6d8e899772b855588722f2') + ); + + $this->assertFalse( + $hasher->verify('bar', 'f149d11c9f6d8e899772b855588722f2') + ); + } + + /** + * Test verification of object hashes + */ + public function testVerifyHashObject() + { + $hasher = $this->createHasher(); + + $obj = new \stdClass; + $obj->foo = 'bar'; + + $this->assertTrue( + $hasher->verify($obj, '2f4162ad4b8774272e452efafd25972f') + ); + + $obj->bar = 'foo'; + + $this->assertFalse( + $hasher->verify($obj, '2f4162ad4b8774272e452efafd25972f') + ); + } + /** * Create hasher * diff --git a/tests/AngryBytes/Hash/Test/PasswordTest.php b/tests/AngryBytes/Hash/Test/PasswordTest.php new file mode 100644 index 0000000..7bf1fe6 --- /dev/null +++ b/tests/AngryBytes/Hash/Test/PasswordTest.php @@ -0,0 +1,114 @@ +createHasher(); + + // Even though the password are equal the hashes should not be + $this->assertNotEquals( + $hasher->hash('foo'), + $hasher->hash('foo') + ); + } + + /** + * Test password hash verification + */ + public function testStringVerify() + { + $hasher = $this->createHasher(); + + $this->assertTrue( + $hasher->verify('foo', '$2y$10$Ch4CoMWaM6KR0ZfC3ZKvc.FAW1U30luCURlyahm/XgzJ4TWDwWPFW') + ); + + $this->assertFalse( + $hasher->verify('bar', '$2y$10$Ch4CoMWaM6KR0ZfC3ZKvc.FAW1U30luCURlyahm/XgzJ4TWDwWPFW') + ); + } + + /** + * Test password hash rehash + */ + public function testRehash() + { + $hasher = $this->createHasher(); + + // Create hash + $hash = $hasher->hash('foo'); + + $this->assertFalse( + $hasher->getHasher()->needsRehash($hash) + ); + + // Adjust the hash cost, this should require a rehash + $hasher->getHasher()->setCost(15); + + $this->assertTrue( + $hasher->getHasher()->needsRehash($hash) + ); + } + + /** + * Test invalid cost factor + * + * @expectedException \InvalidArgumentException + */ + public function testCostTooLow() + { + $hasher = $this->createHasher(); + + $hasher->getHasher()->setCost(2); + } + + /** + * Test invalid cost factor + * + * @expectedException \InvalidArgumentException + */ + public function testCostTooHigh() + { + $hasher = $this->createHasher(); + + $hasher->getHasher()->setCost(42); + } + + /** + * Create hasher + * + * @return Hash + **/ + private function createHasher() + { + // Hasher + return new Hash( + new PasswordHasher + ); + } +} + diff --git a/tests/AngryBytes/Hash/Test/RandomStringTest.php b/tests/AngryBytes/Hash/Test/RandomStringTest.php deleted file mode 100644 index 7bae7c6..0000000 --- a/tests/AngryBytes/Hash/Test/RandomStringTest.php +++ /dev/null @@ -1,45 +0,0 @@ -assertEquals( - strlen($bytes), - $numberOfBytes, - 'Number of bytes returned should be ' . $numberOfBytes - ); - } - } -} diff --git a/tests/AngryBytes/Hash/Test/TestCase.php b/tests/AngryBytes/Hash/Test/TestCase.php index 14b8bfb..2994494 100644 --- a/tests/AngryBytes/Hash/Test/TestCase.php +++ b/tests/AngryBytes/Hash/Test/TestCase.php @@ -5,23 +5,18 @@ * @category AngryBytes * @package Hash * @subpackage Test - * @copyright Copyright (c) 2010 Angry Bytes BV (http://www.angrybytes.com) + * @copyright Copyright (c) 2007-2016 Angry Bytes BV (http://www.angrybytes.com) */ namespace AngryBytes\Hash\Test; -use \PHPUnit_Framework_TestCase as PUTestCase; - /** - * TestCase - * - * Testing memcached + * Hashing test case * * @category AngryBytes * @package Hash * @subpackage Test */ -abstract class TestCase extends PUTestCase +abstract class TestCase extends \PHPUnit_Framework_TestCase { - }