├── .gitignore ├── .php_cs ├── README.md ├── bin └── ballandchain ├── build.sh ├── composer.json ├── composer.lock ├── demo.php └── lib └── BallAndChain ├── Command └── Build.php └── Hash.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | level(Symfony\CS\FixerInterface::NONE_LEVEL) 14 | ->fixers([ 15 | 'header_comment', 16 | 'linefeed', 17 | 'indentation', 18 | 'elseif', 19 | 'line_after_namespace', 20 | 'lowercase_constants', 21 | 'lowercase_keywords', 22 | 'method_argument_space', 23 | 'single_blank_line_before_namespace', 24 | 'ordered_use', 25 | 'short_array_syntax', 26 | 'single_line_after_imports', 27 | 'visibility', 28 | 'trailing_spaces', 29 | 'concat_with_spaces', 30 | 'align_double_arrow', 31 | 'unused_use', 32 | 'ternary_spaces', 33 | 'remove_leading_slash_use', 34 | 'remove_lines_between_uses', 35 | 'phpdoc_indent', 36 | 'phpdoc_no_access', 37 | 'phpdoc_params', 38 | 'phpdoc_scalar', 39 | 'phpdoc_separation', 40 | 'phpdoc_trim', 41 | 'phpdoc_var_without_name', 42 | 'phpdoc_order', 43 | 'no_empty_lines_after_phpdocs', 44 | ]) 45 | ->finder( 46 | Symfony\CS\Finder\DefaultFinder::create() 47 | ->in(__DIR__ . "/lib") 48 | ->in(__DIR__ . "/bin") 49 | ) 50 | ; 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BallAndChain 2 | ============ 3 | 4 | This is a PHP implementation of [BallAndChain](https://www.youtube.com/watch?v=GfyM8lFkjo8). 5 | 6 | The only difference in algorithms are as follows: 7 | 8 | 1. This uses SHA-2 instead of SHA-3 due to availability in PHP 9 | 2. This uses a combined hash format that encodes the settings with a version identifier 10 | 11 | ## Generating A Seed File 12 | 13 | First, you need to generate a "seed file". This is a large random file that is the basis for security. 14 | 15 | Ideally you want to make this file as large as you possibly can (10gb should be the bare minimum). If you can hit 1TB that's better. 10TB is even better! 16 | 17 | There are three options to generating this file. You can do it yourself with `dd`: 18 | 19 | # Our target is 10 * 2**30 or 10GB. For 100GB change to 100 * 2**30 20 | $ dd if=/dev/urandom of=/path/to/file bs=$((64 * 2**20)) count=$(((10 * 2**30) / (64 * 2**20))) 21 | 22 | You can use openssl 23 | 24 | $ openssl rand -out /path/to/file $((10 * 2**30)) 25 | 26 | Or you can use the ballandchain tool to generate (the slowest method) 27 | 28 | $ bin/ballandchain build 10G /path/to/file 29 | 30 | ## Usage 31 | 32 | To use, create a new `Hash` instance by passing the file to the constructor: 33 | 34 | $hash = new BallAndChain\Hash('/path/to/file'); 35 | 36 | Then, to hash a new password: 37 | 38 | $hashed = $hash->create($password); 39 | 40 | And finally, to validate: 41 | 42 | if ($hash->verify($password, $hashed)) { 43 | // Success! 44 | } 45 | 46 | It's that simple! 47 | 48 | ## Output Size 49 | 50 | The output will change depending on the settings you provide. With a 10gb file, and using default settings, the output will be 124 bytes (characters) wide. 51 | 52 | The output can get quite big depending on filesize and number of rounds specified. 53 | 54 | ## Options 55 | 56 | There are a few options that you can pass to `->create()`: 57 | 58 | * rounds 59 | 60 | This is the number of data pointers to lookup when building the hash. It is expressed as a power-of-2. The minimum is `2` (resulting in using 4 pointers). The arbitrary maximum is 62 (maximum 64 bit signed integer power of 2). The default is 3, using 8 pointers. 61 | 62 | Realistically, values of greater than 10 will be useless as the generated hash size increases drastically (by a factor of 2 each time). 63 | 64 | Increasing this number will provide brute-forcing protection, as it doubles the number of I/O operations required to hash a password. This can provide additional protections if the seed file is leaked to an attacker. 65 | 66 | * pointerSize 67 | 68 | This is the size of the pointer to generate. The default will detect the smallest pointer required to access the entire file (a 255 byte file will use a pointer size of 1, a 10GB file will use 5 bytes). 69 | 70 | You can override the default, but beware that setting too short of a size will raise an error. 71 | 72 | * dataSize 73 | 74 | This is the power-of-2 amount of data to pull from each pointed location. The default is 4 (16 bytes). Increasing this will *not* increase the output size of the result. 75 | 76 | Increasing this number will provide brute-forcing protection as it increases the amount of I/O bandwidth consumed in a hashing operation. 77 | 78 | 79 | ## How this works 80 | 81 | * The password is hashed using SHA-256 to create a cipher key. 82 | * Each round generates a random pointer 83 | * Each random pointer is looked up in the file and read into a data array. 84 | * All of the collected data points are hashed using SHA-256 85 | * The pointers and the hashed data points are encrypted using the cipher key. 86 | 87 | In pseudo-code: 88 | 89 | function hash(password) { 90 | key = sha256(password) 91 | pointers = '' 92 | data = '' 93 | for (round in rounds) { 94 | pointer = random_pointer() 95 | pointers = pointers + pointer 96 | data = data + read(pointer) 97 | } 98 | iv = random_iv() 99 | return encrypt(key, iv, pointers + sha256(data)) 100 | } 101 | 102 | To validate a password, you decrypt the ciphertext to get the list of pointers. Then you lookup the pointers to rebuild the data array, and finally verify the hash of data matches the encrypted hash of data. 103 | 104 | Watch the video linked above. It's worth it. 105 | 106 | ## Optimal Settings 107 | 108 | Ideally, you would use as large of a file as possible. 10TB is ideal. You want a file so large that an attacker can't copy it. 109 | 110 | Besides making the file large, you can also restrict the bandwidth to the server with the file. This introduces more potential security holes, but it may be a valid option. 111 | 112 | If your file is extremely large, the default settings should suffice. If we assume that an attacker cannot download the file, then rounds=3 and dataSize=4 are sufficient. 113 | 114 | If however there's a risk of file download, we can increase the two parameters to provide additional brute force protection. 115 | 116 | Increasing rounds will increase the number of I/O operations that an attacker needs to do for each hash. Increasing dataSize will put stress on the I/O throughput the attacker needs for each hash. 117 | 118 | To see how they are related, let's pick rounds=8 and dataSize=13, and look at what happens: 119 | 120 | * The attacker needs to do 256 read operations per hash. On a modern SSD, that will limit them to approximately 400 hashes per second per SSD. This is because the current fastest SSDs can do approximately 100,000 random 4kb block reads per second. 121 | 122 | * The attacker needs to read 8192 bytes for each read operation. Since the 8192 bytes requires 2 block reads, we would expect approximately 200 hashes per second per SSD. 123 | 124 | Combining the two, we'd expect an attacker to have approximately 200 hashes per second per SSD. That's quite good. However, if we dropped the dataSize to 12, the attacker can double their hashes per second. As a rule-of-thumb, below data size of 12, the size will have no effect on hash rate. Above 12, it will cause the hash rate to be cut in half for each step up. So we'd expect 14 to be approximately 100 hashes per second. 125 | 126 | If an attacker can fit the seed file into main memory, things change a bit. If we only look at bandwidth constraints (and not IOPS constraints), then we'd expect approximately 10gb/s per memory module. Meaning that using our prior settings, we'd expect approximately 1.3 million hashes per second (note this is an upper bound and assumes there is no overhead to random multiple block reads). Severely less than a simple SHA-1, but still too high for comfort. This is why making the file large and protecting it is incredibly important. -------------------------------------------------------------------------------- /bin/ballandchain: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | = 0; $i--) { 9 | $file = __DIR__ . str_repeat('/..', $i) . "/vendor/autoload.php"; 10 | if (file_exists($file)) { 11 | $found = true; 12 | require_once $file; 13 | break; 14 | } 15 | } 16 | 17 | if (!$found) { 18 | fwrite(STDERR, 'You need to setup the project dependencies using Composer' . PHP_EOL); 19 | die(1); 20 | } 21 | 22 | $app = new \Cilex\Application("BallAndChain"); 23 | $app->command(new \BallAndChain\Command\Build); 24 | $app->run(); 25 | 26 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | vendor/bin/php-cs-fixer fix -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ircmaxell/ballandchain", 3 | "description": "PHP Implementation of Ball And Chain", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Anthony Ferrara", 8 | "email": "ircmaxell@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=5.5", 13 | "cilex/cilex": "1.*", 14 | "paragonie/random_compat": "dev-master", 15 | "indigophp/hash-compat": "^1.1@dev" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "^4.7", 19 | "fabpot/php-cs-fixer": "1.*" 20 | }, 21 | "minimum-stability": "dev", 22 | "autoload": { 23 | "psr-4": { 24 | "BallAndChain\\": "lib/BallAndChain/" 25 | } 26 | }, 27 | "bin": [ 28 | "bin/ballandchain" 29 | ] 30 | 31 | } 32 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "ea10854ef41152d91829efe899913abc", 8 | "packages": [ 9 | { 10 | "name": "cilex/cilex", 11 | "version": "1.1.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/Cilex/Cilex.git", 15 | "reference": "7acd965a609a56d0345e8b6071c261fbdb926cb5" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/Cilex/Cilex/zipball/7acd965a609a56d0345e8b6071c261fbdb926cb5", 20 | "reference": "7acd965a609a56d0345e8b6071c261fbdb926cb5", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "cilex/console-service-provider": "1.*", 25 | "php": ">=5.3.3", 26 | "pimple/pimple": "~1.0", 27 | "symfony/finder": "~2.1", 28 | "symfony/process": "~2.1" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "3.7.*", 32 | "symfony/validator": "~2.1" 33 | }, 34 | "suggest": { 35 | "monolog/monolog": ">=1.0.0", 36 | "symfony/validator": ">=1.0.0", 37 | "symfony/yaml": ">=1.0.0" 38 | }, 39 | "type": "library", 40 | "extra": { 41 | "branch-alias": { 42 | "dev-master": "1.0-dev" 43 | } 44 | }, 45 | "autoload": { 46 | "psr-0": { 47 | "Cilex": "src/" 48 | } 49 | }, 50 | "notification-url": "https://packagist.org/downloads/", 51 | "license": [ 52 | "MIT" 53 | ], 54 | "authors": [ 55 | { 56 | "name": "Mike van Riel", 57 | "email": "mike.vanriel@naenius.com" 58 | } 59 | ], 60 | "description": "The PHP micro-framework for Command line tools based on the Symfony2 Components", 61 | "homepage": "http://cilex.github.com", 62 | "keywords": [ 63 | "cli", 64 | "microframework" 65 | ], 66 | "time": "2014-03-29 14:03:13" 67 | }, 68 | { 69 | "name": "cilex/console-service-provider", 70 | "version": "1.0.0", 71 | "source": { 72 | "type": "git", 73 | "url": "https://github.com/Cilex/console-service-provider.git", 74 | "reference": "25ee3d1875243d38e1a3448ff94bdf944f70d24e" 75 | }, 76 | "dist": { 77 | "type": "zip", 78 | "url": "https://api.github.com/repos/Cilex/console-service-provider/zipball/25ee3d1875243d38e1a3448ff94bdf944f70d24e", 79 | "reference": "25ee3d1875243d38e1a3448ff94bdf944f70d24e", 80 | "shasum": "" 81 | }, 82 | "require": { 83 | "php": ">=5.3.3", 84 | "pimple/pimple": "1.*@dev", 85 | "symfony/console": "~2.1" 86 | }, 87 | "require-dev": { 88 | "cilex/cilex": "1.*@dev", 89 | "silex/silex": "1.*@dev" 90 | }, 91 | "type": "library", 92 | "extra": { 93 | "branch-alias": { 94 | "dev-master": "1.0-dev" 95 | } 96 | }, 97 | "autoload": { 98 | "psr-0": { 99 | "Cilex\\Provider\\Console": "src" 100 | } 101 | }, 102 | "notification-url": "https://packagist.org/downloads/", 103 | "license": [ 104 | "MIT" 105 | ], 106 | "authors": [ 107 | { 108 | "name": "Beau Simensen", 109 | "email": "beau@dflydev.com", 110 | "homepage": "http://beausimensen.com" 111 | }, 112 | { 113 | "name": "Mike van Riel", 114 | "email": "mike.vanriel@naenius.com" 115 | } 116 | ], 117 | "description": "Console Service Provider", 118 | "keywords": [ 119 | "cilex", 120 | "console", 121 | "pimple", 122 | "service-provider", 123 | "silex" 124 | ], 125 | "time": "2012-12-19 10:50:58" 126 | }, 127 | { 128 | "name": "indigophp/hash-compat", 129 | "version": "dev-master", 130 | "source": { 131 | "type": "git", 132 | "url": "https://github.com/indigophp/hash-compat.git", 133 | "reference": "2eac957326cadb7b6de9a6c924c37c7a4eaac771" 134 | }, 135 | "dist": { 136 | "type": "zip", 137 | "url": "https://api.github.com/repos/indigophp/hash-compat/zipball/2eac957326cadb7b6de9a6c924c37c7a4eaac771", 138 | "reference": "2eac957326cadb7b6de9a6c924c37c7a4eaac771", 139 | "shasum": "" 140 | }, 141 | "require": { 142 | "php": ">=5.3" 143 | }, 144 | "require-dev": { 145 | "phpunit/phpunit": "~4.4" 146 | }, 147 | "type": "library", 148 | "extra": { 149 | "branch-alias": { 150 | "dev-master": "1.1-dev" 151 | } 152 | }, 153 | "autoload": { 154 | "files": [ 155 | "src/hash_equals.php", 156 | "src/hash_pbkdf2.php" 157 | ] 158 | }, 159 | "notification-url": "https://packagist.org/downloads/", 160 | "license": [ 161 | "MIT" 162 | ], 163 | "authors": [ 164 | { 165 | "name": "Márk Sági-Kazár", 166 | "email": "mark.sagikazar@gmail.com" 167 | } 168 | ], 169 | "description": "Backports hash_* functionality to older PHP versions", 170 | "homepage": "https://indigophp.com", 171 | "keywords": [ 172 | "hash", 173 | "hash_equals", 174 | "hash_pbkdf2" 175 | ], 176 | "time": "2015-07-14 21:53:39" 177 | }, 178 | { 179 | "name": "paragonie/random_compat", 180 | "version": "dev-master", 181 | "source": { 182 | "type": "git", 183 | "url": "https://github.com/paragonie/random_compat.git", 184 | "reference": "a04d3e944082f3ec22d88faf86481a40b025ef6d" 185 | }, 186 | "dist": { 187 | "type": "zip", 188 | "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a04d3e944082f3ec22d88faf86481a40b025ef6d", 189 | "reference": "a04d3e944082f3ec22d88faf86481a40b025ef6d", 190 | "shasum": "" 191 | }, 192 | "type": "library", 193 | "autoload": { 194 | "files": [ 195 | "lib/random.php" 196 | ] 197 | }, 198 | "notification-url": "https://packagist.org/downloads/", 199 | "license": [ 200 | "MIT" 201 | ], 202 | "authors": [ 203 | { 204 | "name": "Paragon Initiative Enterprises", 205 | "email": "security@paragonie.com", 206 | "homepage": "https://paragonie.com" 207 | } 208 | ], 209 | "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", 210 | "keywords": [ 211 | "csprng", 212 | "pseudorandom", 213 | "random" 214 | ], 215 | "time": "2015-08-06 18:07:29" 216 | }, 217 | { 218 | "name": "pimple/pimple", 219 | "version": "1.1.x-dev", 220 | "source": { 221 | "type": "git", 222 | "url": "https://github.com/silexphp/Pimple.git", 223 | "reference": "bc2fc12cdf1f29bcad9e650d493a54a8fd1f3d85" 224 | }, 225 | "dist": { 226 | "type": "zip", 227 | "url": "https://api.github.com/repos/silexphp/Pimple/zipball/bc2fc12cdf1f29bcad9e650d493a54a8fd1f3d85", 228 | "reference": "bc2fc12cdf1f29bcad9e650d493a54a8fd1f3d85", 229 | "shasum": "" 230 | }, 231 | "require": { 232 | "php": ">=5.3.0" 233 | }, 234 | "type": "library", 235 | "extra": { 236 | "branch-alias": { 237 | "dev-master": "1.1.x-dev" 238 | } 239 | }, 240 | "autoload": { 241 | "psr-0": { 242 | "Pimple": "lib/" 243 | } 244 | }, 245 | "notification-url": "https://packagist.org/downloads/", 246 | "license": [ 247 | "MIT" 248 | ], 249 | "authors": [ 250 | { 251 | "name": "Fabien Potencier", 252 | "email": "fabien@symfony.com" 253 | } 254 | ], 255 | "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", 256 | "homepage": "http://pimple.sensiolabs.org", 257 | "keywords": [ 258 | "container", 259 | "dependency injection" 260 | ], 261 | "time": "2014-04-20 07:24:09" 262 | }, 263 | { 264 | "name": "symfony/console", 265 | "version": "2.8.x-dev", 266 | "source": { 267 | "type": "git", 268 | "url": "https://github.com/symfony/Console.git", 269 | "reference": "eb0fcf6f58054ac9d07539451e27aa4e17aebfe0" 270 | }, 271 | "dist": { 272 | "type": "zip", 273 | "url": "https://api.github.com/repos/symfony/Console/zipball/eb0fcf6f58054ac9d07539451e27aa4e17aebfe0", 274 | "reference": "eb0fcf6f58054ac9d07539451e27aa4e17aebfe0", 275 | "shasum": "" 276 | }, 277 | "require": { 278 | "php": ">=5.3.9" 279 | }, 280 | "require-dev": { 281 | "psr/log": "~1.0", 282 | "symfony/event-dispatcher": "~2.1|~3.0.0", 283 | "symfony/phpunit-bridge": "~2.7|~3.0.0", 284 | "symfony/process": "~2.1|~3.0.0" 285 | }, 286 | "suggest": { 287 | "psr/log": "For using the console logger", 288 | "symfony/event-dispatcher": "", 289 | "symfony/process": "" 290 | }, 291 | "type": "library", 292 | "extra": { 293 | "branch-alias": { 294 | "dev-master": "2.8-dev" 295 | } 296 | }, 297 | "autoload": { 298 | "psr-4": { 299 | "Symfony\\Component\\Console\\": "" 300 | } 301 | }, 302 | "notification-url": "https://packagist.org/downloads/", 303 | "license": [ 304 | "MIT" 305 | ], 306 | "authors": [ 307 | { 308 | "name": "Fabien Potencier", 309 | "email": "fabien@symfony.com" 310 | }, 311 | { 312 | "name": "Symfony Community", 313 | "homepage": "https://symfony.com/contributors" 314 | } 315 | ], 316 | "description": "Symfony Console Component", 317 | "homepage": "https://symfony.com", 318 | "time": "2015-08-04 15:59:05" 319 | }, 320 | { 321 | "name": "symfony/finder", 322 | "version": "2.8.x-dev", 323 | "source": { 324 | "type": "git", 325 | "url": "https://github.com/symfony/Finder.git", 326 | "reference": "8712d5e8c4ad65e6b936ed9b6a581e5e6a87fddf" 327 | }, 328 | "dist": { 329 | "type": "zip", 330 | "url": "https://api.github.com/repos/symfony/Finder/zipball/8712d5e8c4ad65e6b936ed9b6a581e5e6a87fddf", 331 | "reference": "8712d5e8c4ad65e6b936ed9b6a581e5e6a87fddf", 332 | "shasum": "" 333 | }, 334 | "require": { 335 | "php": ">=5.3.9" 336 | }, 337 | "require-dev": { 338 | "symfony/phpunit-bridge": "~2.7|~3.0.0" 339 | }, 340 | "type": "library", 341 | "extra": { 342 | "branch-alias": { 343 | "dev-master": "2.8-dev" 344 | } 345 | }, 346 | "autoload": { 347 | "psr-4": { 348 | "Symfony\\Component\\Finder\\": "" 349 | } 350 | }, 351 | "notification-url": "https://packagist.org/downloads/", 352 | "license": [ 353 | "MIT" 354 | ], 355 | "authors": [ 356 | { 357 | "name": "Fabien Potencier", 358 | "email": "fabien@symfony.com" 359 | }, 360 | { 361 | "name": "Symfony Community", 362 | "homepage": "https://symfony.com/contributors" 363 | } 364 | ], 365 | "description": "Symfony Finder Component", 366 | "homepage": "https://symfony.com", 367 | "time": "2015-07-09 16:11:14" 368 | }, 369 | { 370 | "name": "symfony/process", 371 | "version": "2.8.x-dev", 372 | "source": { 373 | "type": "git", 374 | "url": "https://github.com/symfony/Process.git", 375 | "reference": "25d74c90d79e66905013714d8d188e4ccb5ff466" 376 | }, 377 | "dist": { 378 | "type": "zip", 379 | "url": "https://api.github.com/repos/symfony/Process/zipball/25d74c90d79e66905013714d8d188e4ccb5ff466", 380 | "reference": "25d74c90d79e66905013714d8d188e4ccb5ff466", 381 | "shasum": "" 382 | }, 383 | "require": { 384 | "php": ">=5.3.9" 385 | }, 386 | "require-dev": { 387 | "symfony/phpunit-bridge": "~2.7|~3.0.0" 388 | }, 389 | "type": "library", 390 | "extra": { 391 | "branch-alias": { 392 | "dev-master": "2.8-dev" 393 | } 394 | }, 395 | "autoload": { 396 | "psr-4": { 397 | "Symfony\\Component\\Process\\": "" 398 | } 399 | }, 400 | "notification-url": "https://packagist.org/downloads/", 401 | "license": [ 402 | "MIT" 403 | ], 404 | "authors": [ 405 | { 406 | "name": "Fabien Potencier", 407 | "email": "fabien@symfony.com" 408 | }, 409 | { 410 | "name": "Symfony Community", 411 | "homepage": "https://symfony.com/contributors" 412 | } 413 | ], 414 | "description": "Symfony Process Component", 415 | "homepage": "https://symfony.com", 416 | "time": "2015-07-01 14:16:54" 417 | } 418 | ], 419 | "packages-dev": [ 420 | { 421 | "name": "doctrine/instantiator", 422 | "version": "dev-master", 423 | "source": { 424 | "type": "git", 425 | "url": "https://github.com/doctrine/instantiator.git", 426 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 427 | }, 428 | "dist": { 429 | "type": "zip", 430 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 431 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 432 | "shasum": "" 433 | }, 434 | "require": { 435 | "php": ">=5.3,<8.0-DEV" 436 | }, 437 | "require-dev": { 438 | "athletic/athletic": "~0.1.8", 439 | "ext-pdo": "*", 440 | "ext-phar": "*", 441 | "phpunit/phpunit": "~4.0", 442 | "squizlabs/php_codesniffer": "~2.0" 443 | }, 444 | "type": "library", 445 | "extra": { 446 | "branch-alias": { 447 | "dev-master": "1.0.x-dev" 448 | } 449 | }, 450 | "autoload": { 451 | "psr-4": { 452 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 453 | } 454 | }, 455 | "notification-url": "https://packagist.org/downloads/", 456 | "license": [ 457 | "MIT" 458 | ], 459 | "authors": [ 460 | { 461 | "name": "Marco Pivetta", 462 | "email": "ocramius@gmail.com", 463 | "homepage": "http://ocramius.github.com/" 464 | } 465 | ], 466 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 467 | "homepage": "https://github.com/doctrine/instantiator", 468 | "keywords": [ 469 | "constructor", 470 | "instantiate" 471 | ], 472 | "time": "2015-06-14 21:17:01" 473 | }, 474 | { 475 | "name": "fabpot/php-cs-fixer", 476 | "version": "1.11.x-dev", 477 | "source": { 478 | "type": "git", 479 | "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", 480 | "reference": "19381741438f2b509928920dc7b5aa3fb00cf0d3" 481 | }, 482 | "dist": { 483 | "type": "zip", 484 | "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/19381741438f2b509928920dc7b5aa3fb00cf0d3", 485 | "reference": "19381741438f2b509928920dc7b5aa3fb00cf0d3", 486 | "shasum": "" 487 | }, 488 | "require": { 489 | "ext-tokenizer": "*", 490 | "php": ">=5.3.6", 491 | "sebastian/diff": "~1.1", 492 | "symfony/console": "~2.3", 493 | "symfony/event-dispatcher": "~2.1", 494 | "symfony/filesystem": "~2.1", 495 | "symfony/finder": "~2.1", 496 | "symfony/process": "~2.3", 497 | "symfony/stopwatch": "~2.5" 498 | }, 499 | "require-dev": { 500 | "satooshi/php-coveralls": "0.7.*@dev" 501 | }, 502 | "bin": [ 503 | "php-cs-fixer" 504 | ], 505 | "type": "application", 506 | "autoload": { 507 | "psr-4": { 508 | "Symfony\\CS\\": "Symfony/CS/" 509 | } 510 | }, 511 | "notification-url": "https://packagist.org/downloads/", 512 | "license": [ 513 | "MIT" 514 | ], 515 | "authors": [ 516 | { 517 | "name": "Dariusz Rumiński", 518 | "email": "dariusz.ruminski@gmail.com" 519 | }, 520 | { 521 | "name": "Fabien Potencier", 522 | "email": "fabien@symfony.com" 523 | } 524 | ], 525 | "description": "A tool to automatically fix PHP code style", 526 | "time": "2015-08-08 14:35:28" 527 | }, 528 | { 529 | "name": "phpdocumentor/reflection-docblock", 530 | "version": "2.0.4", 531 | "source": { 532 | "type": "git", 533 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 534 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" 535 | }, 536 | "dist": { 537 | "type": "zip", 538 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", 539 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", 540 | "shasum": "" 541 | }, 542 | "require": { 543 | "php": ">=5.3.3" 544 | }, 545 | "require-dev": { 546 | "phpunit/phpunit": "~4.0" 547 | }, 548 | "suggest": { 549 | "dflydev/markdown": "~1.0", 550 | "erusev/parsedown": "~1.0" 551 | }, 552 | "type": "library", 553 | "extra": { 554 | "branch-alias": { 555 | "dev-master": "2.0.x-dev" 556 | } 557 | }, 558 | "autoload": { 559 | "psr-0": { 560 | "phpDocumentor": [ 561 | "src/" 562 | ] 563 | } 564 | }, 565 | "notification-url": "https://packagist.org/downloads/", 566 | "license": [ 567 | "MIT" 568 | ], 569 | "authors": [ 570 | { 571 | "name": "Mike van Riel", 572 | "email": "mike.vanriel@naenius.com" 573 | } 574 | ], 575 | "time": "2015-02-03 12:10:50" 576 | }, 577 | { 578 | "name": "phpspec/prophecy", 579 | "version": "dev-master", 580 | "source": { 581 | "type": "git", 582 | "url": "https://github.com/phpspec/prophecy.git", 583 | "reference": "5700f75b23b0dd3495c0f495fe33a5e6717ee160" 584 | }, 585 | "dist": { 586 | "type": "zip", 587 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/5700f75b23b0dd3495c0f495fe33a5e6717ee160", 588 | "reference": "5700f75b23b0dd3495c0f495fe33a5e6717ee160", 589 | "shasum": "" 590 | }, 591 | "require": { 592 | "doctrine/instantiator": "^1.0.2", 593 | "phpdocumentor/reflection-docblock": "~2.0", 594 | "sebastian/comparator": "~1.1" 595 | }, 596 | "require-dev": { 597 | "phpspec/phpspec": "~2.0" 598 | }, 599 | "type": "library", 600 | "extra": { 601 | "branch-alias": { 602 | "dev-master": "1.4.x-dev" 603 | } 604 | }, 605 | "autoload": { 606 | "psr-0": { 607 | "Prophecy\\": "src/" 608 | } 609 | }, 610 | "notification-url": "https://packagist.org/downloads/", 611 | "license": [ 612 | "MIT" 613 | ], 614 | "authors": [ 615 | { 616 | "name": "Konstantin Kudryashov", 617 | "email": "ever.zet@gmail.com", 618 | "homepage": "http://everzet.com" 619 | }, 620 | { 621 | "name": "Marcello Duarte", 622 | "email": "marcello.duarte@gmail.com" 623 | } 624 | ], 625 | "description": "Highly opinionated mocking framework for PHP 5.3+", 626 | "homepage": "https://github.com/phpspec/prophecy", 627 | "keywords": [ 628 | "Double", 629 | "Dummy", 630 | "fake", 631 | "mock", 632 | "spy", 633 | "stub" 634 | ], 635 | "time": "2015-06-30 10:26:46" 636 | }, 637 | { 638 | "name": "phpunit/php-code-coverage", 639 | "version": "dev-master", 640 | "source": { 641 | "type": "git", 642 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 643 | "reference": "40103f9b335f1d84daed099f180b9de12ba080e7" 644 | }, 645 | "dist": { 646 | "type": "zip", 647 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/40103f9b335f1d84daed099f180b9de12ba080e7", 648 | "reference": "40103f9b335f1d84daed099f180b9de12ba080e7", 649 | "shasum": "" 650 | }, 651 | "require": { 652 | "php": ">=5.3.3", 653 | "phpunit/php-file-iterator": "~1.3", 654 | "phpunit/php-text-template": "~1.2", 655 | "phpunit/php-token-stream": "~1.3", 656 | "sebastian/environment": "^1.3.2", 657 | "sebastian/version": "~1.0" 658 | }, 659 | "require-dev": { 660 | "ext-xdebug": ">=2.1.4", 661 | "phpunit/phpunit": "~4" 662 | }, 663 | "suggest": { 664 | "ext-dom": "*", 665 | "ext-xdebug": ">=2.2.1", 666 | "ext-xmlwriter": "*" 667 | }, 668 | "type": "library", 669 | "extra": { 670 | "branch-alias": { 671 | "dev-master": "2.2.x-dev" 672 | } 673 | }, 674 | "autoload": { 675 | "classmap": [ 676 | "src/" 677 | ] 678 | }, 679 | "notification-url": "https://packagist.org/downloads/", 680 | "license": [ 681 | "BSD-3-Clause" 682 | ], 683 | "authors": [ 684 | { 685 | "name": "Sebastian Bergmann", 686 | "email": "sb@sebastian-bergmann.de", 687 | "role": "lead" 688 | } 689 | ], 690 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 691 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 692 | "keywords": [ 693 | "coverage", 694 | "testing", 695 | "xunit" 696 | ], 697 | "time": "2015-08-04 03:45:55" 698 | }, 699 | { 700 | "name": "phpunit/php-file-iterator", 701 | "version": "dev-master", 702 | "source": { 703 | "type": "git", 704 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 705 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" 706 | }, 707 | "dist": { 708 | "type": "zip", 709 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 710 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 711 | "shasum": "" 712 | }, 713 | "require": { 714 | "php": ">=5.3.3" 715 | }, 716 | "type": "library", 717 | "extra": { 718 | "branch-alias": { 719 | "dev-master": "1.4.x-dev" 720 | } 721 | }, 722 | "autoload": { 723 | "classmap": [ 724 | "src/" 725 | ] 726 | }, 727 | "notification-url": "https://packagist.org/downloads/", 728 | "license": [ 729 | "BSD-3-Clause" 730 | ], 731 | "authors": [ 732 | { 733 | "name": "Sebastian Bergmann", 734 | "email": "sb@sebastian-bergmann.de", 735 | "role": "lead" 736 | } 737 | ], 738 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 739 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 740 | "keywords": [ 741 | "filesystem", 742 | "iterator" 743 | ], 744 | "time": "2015-06-21 13:08:43" 745 | }, 746 | { 747 | "name": "phpunit/php-text-template", 748 | "version": "1.2.1", 749 | "source": { 750 | "type": "git", 751 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 752 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 753 | }, 754 | "dist": { 755 | "type": "zip", 756 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 757 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 758 | "shasum": "" 759 | }, 760 | "require": { 761 | "php": ">=5.3.3" 762 | }, 763 | "type": "library", 764 | "autoload": { 765 | "classmap": [ 766 | "src/" 767 | ] 768 | }, 769 | "notification-url": "https://packagist.org/downloads/", 770 | "license": [ 771 | "BSD-3-Clause" 772 | ], 773 | "authors": [ 774 | { 775 | "name": "Sebastian Bergmann", 776 | "email": "sebastian@phpunit.de", 777 | "role": "lead" 778 | } 779 | ], 780 | "description": "Simple template engine.", 781 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 782 | "keywords": [ 783 | "template" 784 | ], 785 | "time": "2015-06-21 13:50:34" 786 | }, 787 | { 788 | "name": "phpunit/php-timer", 789 | "version": "dev-master", 790 | "source": { 791 | "type": "git", 792 | "url": "https://github.com/sebastianbergmann/php-timer.git", 793 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" 794 | }, 795 | "dist": { 796 | "type": "zip", 797 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 798 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 799 | "shasum": "" 800 | }, 801 | "require": { 802 | "php": ">=5.3.3" 803 | }, 804 | "type": "library", 805 | "autoload": { 806 | "classmap": [ 807 | "src/" 808 | ] 809 | }, 810 | "notification-url": "https://packagist.org/downloads/", 811 | "license": [ 812 | "BSD-3-Clause" 813 | ], 814 | "authors": [ 815 | { 816 | "name": "Sebastian Bergmann", 817 | "email": "sb@sebastian-bergmann.de", 818 | "role": "lead" 819 | } 820 | ], 821 | "description": "Utility class for timing", 822 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 823 | "keywords": [ 824 | "timer" 825 | ], 826 | "time": "2015-06-21 08:01:12" 827 | }, 828 | { 829 | "name": "phpunit/php-token-stream", 830 | "version": "dev-master", 831 | "source": { 832 | "type": "git", 833 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 834 | "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9" 835 | }, 836 | "dist": { 837 | "type": "zip", 838 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/7a9b0969488c3c54fd62b4d504b3ec758fd005d9", 839 | "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9", 840 | "shasum": "" 841 | }, 842 | "require": { 843 | "ext-tokenizer": "*", 844 | "php": ">=5.3.3" 845 | }, 846 | "require-dev": { 847 | "phpunit/phpunit": "~4.2" 848 | }, 849 | "type": "library", 850 | "extra": { 851 | "branch-alias": { 852 | "dev-master": "1.4-dev" 853 | } 854 | }, 855 | "autoload": { 856 | "classmap": [ 857 | "src/" 858 | ] 859 | }, 860 | "notification-url": "https://packagist.org/downloads/", 861 | "license": [ 862 | "BSD-3-Clause" 863 | ], 864 | "authors": [ 865 | { 866 | "name": "Sebastian Bergmann", 867 | "email": "sebastian@phpunit.de" 868 | } 869 | ], 870 | "description": "Wrapper around PHP's tokenizer extension.", 871 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 872 | "keywords": [ 873 | "tokenizer" 874 | ], 875 | "time": "2015-06-19 03:43:16" 876 | }, 877 | { 878 | "name": "phpunit/phpunit", 879 | "version": "4.8.x-dev", 880 | "source": { 881 | "type": "git", 882 | "url": "https://github.com/sebastianbergmann/phpunit.git", 883 | "reference": "a65a36e35e2a3b38bd44ca33fcb3feb68757de12" 884 | }, 885 | "dist": { 886 | "type": "zip", 887 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a65a36e35e2a3b38bd44ca33fcb3feb68757de12", 888 | "reference": "a65a36e35e2a3b38bd44ca33fcb3feb68757de12", 889 | "shasum": "" 890 | }, 891 | "require": { 892 | "ext-dom": "*", 893 | "ext-json": "*", 894 | "ext-pcre": "*", 895 | "ext-reflection": "*", 896 | "ext-spl": "*", 897 | "php": ">=5.3.3", 898 | "phpspec/prophecy": "^1.3.1", 899 | "phpunit/php-code-coverage": "~2.1", 900 | "phpunit/php-file-iterator": "~1.4", 901 | "phpunit/php-text-template": "~1.2", 902 | "phpunit/php-timer": ">=1.0.6", 903 | "phpunit/phpunit-mock-objects": "~2.3", 904 | "sebastian/comparator": "~1.1", 905 | "sebastian/diff": "~1.2", 906 | "sebastian/environment": "~1.3", 907 | "sebastian/exporter": "~1.2", 908 | "sebastian/global-state": "~1.0", 909 | "sebastian/version": "~1.0", 910 | "symfony/yaml": "~2.1|~3.0" 911 | }, 912 | "suggest": { 913 | "phpunit/php-invoker": "~1.1" 914 | }, 915 | "bin": [ 916 | "phpunit" 917 | ], 918 | "type": "library", 919 | "extra": { 920 | "branch-alias": { 921 | "dev-master": "4.8.x-dev" 922 | } 923 | }, 924 | "autoload": { 925 | "classmap": [ 926 | "src/" 927 | ] 928 | }, 929 | "notification-url": "https://packagist.org/downloads/", 930 | "license": [ 931 | "BSD-3-Clause" 932 | ], 933 | "authors": [ 934 | { 935 | "name": "Sebastian Bergmann", 936 | "email": "sebastian@phpunit.de", 937 | "role": "lead" 938 | } 939 | ], 940 | "description": "The PHP Unit Testing framework.", 941 | "homepage": "https://phpunit.de/", 942 | "keywords": [ 943 | "phpunit", 944 | "testing", 945 | "xunit" 946 | ], 947 | "time": "2015-08-07 13:02:15" 948 | }, 949 | { 950 | "name": "phpunit/phpunit-mock-objects", 951 | "version": "2.3.x-dev", 952 | "source": { 953 | "type": "git", 954 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 955 | "reference": "dccd8011934fba6fb77c7182c74926ad58c9ecd1" 956 | }, 957 | "dist": { 958 | "type": "zip", 959 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/dccd8011934fba6fb77c7182c74926ad58c9ecd1", 960 | "reference": "dccd8011934fba6fb77c7182c74926ad58c9ecd1", 961 | "shasum": "" 962 | }, 963 | "require": { 964 | "doctrine/instantiator": "^1.0.2", 965 | "php": ">=5.3.3", 966 | "phpunit/php-text-template": "~1.2", 967 | "sebastian/exporter": "~1.2" 968 | }, 969 | "require-dev": { 970 | "phpunit/phpunit": "~4.4" 971 | }, 972 | "suggest": { 973 | "ext-soap": "*" 974 | }, 975 | "type": "library", 976 | "extra": { 977 | "branch-alias": { 978 | "dev-master": "2.3.x-dev" 979 | } 980 | }, 981 | "autoload": { 982 | "classmap": [ 983 | "src/" 984 | ] 985 | }, 986 | "notification-url": "https://packagist.org/downloads/", 987 | "license": [ 988 | "BSD-3-Clause" 989 | ], 990 | "authors": [ 991 | { 992 | "name": "Sebastian Bergmann", 993 | "email": "sb@sebastian-bergmann.de", 994 | "role": "lead" 995 | } 996 | ], 997 | "description": "Mock Object library for PHPUnit", 998 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 999 | "keywords": [ 1000 | "mock", 1001 | "xunit" 1002 | ], 1003 | "time": "2015-08-09 04:15:55" 1004 | }, 1005 | { 1006 | "name": "sebastian/comparator", 1007 | "version": "dev-master", 1008 | "source": { 1009 | "type": "git", 1010 | "url": "https://github.com/sebastianbergmann/comparator.git", 1011 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" 1012 | }, 1013 | "dist": { 1014 | "type": "zip", 1015 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", 1016 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", 1017 | "shasum": "" 1018 | }, 1019 | "require": { 1020 | "php": ">=5.3.3", 1021 | "sebastian/diff": "~1.2", 1022 | "sebastian/exporter": "~1.2" 1023 | }, 1024 | "require-dev": { 1025 | "phpunit/phpunit": "~4.4" 1026 | }, 1027 | "type": "library", 1028 | "extra": { 1029 | "branch-alias": { 1030 | "dev-master": "1.2.x-dev" 1031 | } 1032 | }, 1033 | "autoload": { 1034 | "classmap": [ 1035 | "src/" 1036 | ] 1037 | }, 1038 | "notification-url": "https://packagist.org/downloads/", 1039 | "license": [ 1040 | "BSD-3-Clause" 1041 | ], 1042 | "authors": [ 1043 | { 1044 | "name": "Jeff Welch", 1045 | "email": "whatthejeff@gmail.com" 1046 | }, 1047 | { 1048 | "name": "Volker Dusch", 1049 | "email": "github@wallbash.com" 1050 | }, 1051 | { 1052 | "name": "Bernhard Schussek", 1053 | "email": "bschussek@2bepublished.at" 1054 | }, 1055 | { 1056 | "name": "Sebastian Bergmann", 1057 | "email": "sebastian@phpunit.de" 1058 | } 1059 | ], 1060 | "description": "Provides the functionality to compare PHP values for equality", 1061 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 1062 | "keywords": [ 1063 | "comparator", 1064 | "compare", 1065 | "equality" 1066 | ], 1067 | "time": "2015-07-26 15:48:44" 1068 | }, 1069 | { 1070 | "name": "sebastian/diff", 1071 | "version": "dev-master", 1072 | "source": { 1073 | "type": "git", 1074 | "url": "https://github.com/sebastianbergmann/diff.git", 1075 | "reference": "6899b3e33bfbd386d88b5eea5f65f563e8793051" 1076 | }, 1077 | "dist": { 1078 | "type": "zip", 1079 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6899b3e33bfbd386d88b5eea5f65f563e8793051", 1080 | "reference": "6899b3e33bfbd386d88b5eea5f65f563e8793051", 1081 | "shasum": "" 1082 | }, 1083 | "require": { 1084 | "php": ">=5.3.3" 1085 | }, 1086 | "require-dev": { 1087 | "phpunit/phpunit": "~4.2" 1088 | }, 1089 | "type": "library", 1090 | "extra": { 1091 | "branch-alias": { 1092 | "dev-master": "1.3-dev" 1093 | } 1094 | }, 1095 | "autoload": { 1096 | "classmap": [ 1097 | "src/" 1098 | ] 1099 | }, 1100 | "notification-url": "https://packagist.org/downloads/", 1101 | "license": [ 1102 | "BSD-3-Clause" 1103 | ], 1104 | "authors": [ 1105 | { 1106 | "name": "Kore Nordmann", 1107 | "email": "mail@kore-nordmann.de" 1108 | }, 1109 | { 1110 | "name": "Sebastian Bergmann", 1111 | "email": "sebastian@phpunit.de" 1112 | } 1113 | ], 1114 | "description": "Diff implementation", 1115 | "homepage": "http://www.github.com/sebastianbergmann/diff", 1116 | "keywords": [ 1117 | "diff" 1118 | ], 1119 | "time": "2015-06-22 14:15:55" 1120 | }, 1121 | { 1122 | "name": "sebastian/environment", 1123 | "version": "dev-master", 1124 | "source": { 1125 | "type": "git", 1126 | "url": "https://github.com/sebastianbergmann/environment.git", 1127 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44" 1128 | }, 1129 | "dist": { 1130 | "type": "zip", 1131 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44", 1132 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44", 1133 | "shasum": "" 1134 | }, 1135 | "require": { 1136 | "php": ">=5.3.3" 1137 | }, 1138 | "require-dev": { 1139 | "phpunit/phpunit": "~4.4" 1140 | }, 1141 | "type": "library", 1142 | "extra": { 1143 | "branch-alias": { 1144 | "dev-master": "1.3.x-dev" 1145 | } 1146 | }, 1147 | "autoload": { 1148 | "classmap": [ 1149 | "src/" 1150 | ] 1151 | }, 1152 | "notification-url": "https://packagist.org/downloads/", 1153 | "license": [ 1154 | "BSD-3-Clause" 1155 | ], 1156 | "authors": [ 1157 | { 1158 | "name": "Sebastian Bergmann", 1159 | "email": "sebastian@phpunit.de" 1160 | } 1161 | ], 1162 | "description": "Provides functionality to handle HHVM/PHP environments", 1163 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1164 | "keywords": [ 1165 | "Xdebug", 1166 | "environment", 1167 | "hhvm" 1168 | ], 1169 | "time": "2015-08-03 06:14:51" 1170 | }, 1171 | { 1172 | "name": "sebastian/exporter", 1173 | "version": "dev-master", 1174 | "source": { 1175 | "type": "git", 1176 | "url": "https://github.com/sebastianbergmann/exporter.git", 1177 | "reference": "f88f8936517d54ae6d589166810877fb2015d0a2" 1178 | }, 1179 | "dist": { 1180 | "type": "zip", 1181 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f88f8936517d54ae6d589166810877fb2015d0a2", 1182 | "reference": "f88f8936517d54ae6d589166810877fb2015d0a2", 1183 | "shasum": "" 1184 | }, 1185 | "require": { 1186 | "php": ">=5.3.3", 1187 | "sebastian/recursion-context": "~1.0" 1188 | }, 1189 | "require-dev": { 1190 | "ext-mbstring": "*", 1191 | "phpunit/phpunit": "~4.4" 1192 | }, 1193 | "type": "library", 1194 | "extra": { 1195 | "branch-alias": { 1196 | "dev-master": "1.3.x-dev" 1197 | } 1198 | }, 1199 | "autoload": { 1200 | "classmap": [ 1201 | "src/" 1202 | ] 1203 | }, 1204 | "notification-url": "https://packagist.org/downloads/", 1205 | "license": [ 1206 | "BSD-3-Clause" 1207 | ], 1208 | "authors": [ 1209 | { 1210 | "name": "Jeff Welch", 1211 | "email": "whatthejeff@gmail.com" 1212 | }, 1213 | { 1214 | "name": "Volker Dusch", 1215 | "email": "github@wallbash.com" 1216 | }, 1217 | { 1218 | "name": "Bernhard Schussek", 1219 | "email": "bschussek@2bepublished.at" 1220 | }, 1221 | { 1222 | "name": "Sebastian Bergmann", 1223 | "email": "sebastian@phpunit.de" 1224 | }, 1225 | { 1226 | "name": "Adam Harvey", 1227 | "email": "aharvey@php.net" 1228 | } 1229 | ], 1230 | "description": "Provides the functionality to export PHP variables for visualization", 1231 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1232 | "keywords": [ 1233 | "export", 1234 | "exporter" 1235 | ], 1236 | "time": "2015-08-09 04:23:41" 1237 | }, 1238 | { 1239 | "name": "sebastian/global-state", 1240 | "version": "dev-master", 1241 | "source": { 1242 | "type": "git", 1243 | "url": "https://github.com/sebastianbergmann/global-state.git", 1244 | "reference": "23af31f402993cfd94e99cbc4b782e9a78eb0e97" 1245 | }, 1246 | "dist": { 1247 | "type": "zip", 1248 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23af31f402993cfd94e99cbc4b782e9a78eb0e97", 1249 | "reference": "23af31f402993cfd94e99cbc4b782e9a78eb0e97", 1250 | "shasum": "" 1251 | }, 1252 | "require": { 1253 | "php": ">=5.3.3" 1254 | }, 1255 | "require-dev": { 1256 | "phpunit/phpunit": "~4.2" 1257 | }, 1258 | "suggest": { 1259 | "ext-uopz": "*" 1260 | }, 1261 | "type": "library", 1262 | "extra": { 1263 | "branch-alias": { 1264 | "dev-master": "1.0-dev" 1265 | } 1266 | }, 1267 | "autoload": { 1268 | "classmap": [ 1269 | "src/" 1270 | ] 1271 | }, 1272 | "notification-url": "https://packagist.org/downloads/", 1273 | "license": [ 1274 | "BSD-3-Clause" 1275 | ], 1276 | "authors": [ 1277 | { 1278 | "name": "Sebastian Bergmann", 1279 | "email": "sebastian@phpunit.de" 1280 | } 1281 | ], 1282 | "description": "Snapshotting of global state", 1283 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1284 | "keywords": [ 1285 | "global state" 1286 | ], 1287 | "time": "2015-06-21 15:11:22" 1288 | }, 1289 | { 1290 | "name": "sebastian/recursion-context", 1291 | "version": "dev-master", 1292 | "source": { 1293 | "type": "git", 1294 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1295 | "reference": "994d4a811bafe801fb06dccbee797863ba2792ba" 1296 | }, 1297 | "dist": { 1298 | "type": "zip", 1299 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba", 1300 | "reference": "994d4a811bafe801fb06dccbee797863ba2792ba", 1301 | "shasum": "" 1302 | }, 1303 | "require": { 1304 | "php": ">=5.3.3" 1305 | }, 1306 | "require-dev": { 1307 | "phpunit/phpunit": "~4.4" 1308 | }, 1309 | "type": "library", 1310 | "extra": { 1311 | "branch-alias": { 1312 | "dev-master": "1.0.x-dev" 1313 | } 1314 | }, 1315 | "autoload": { 1316 | "classmap": [ 1317 | "src/" 1318 | ] 1319 | }, 1320 | "notification-url": "https://packagist.org/downloads/", 1321 | "license": [ 1322 | "BSD-3-Clause" 1323 | ], 1324 | "authors": [ 1325 | { 1326 | "name": "Jeff Welch", 1327 | "email": "whatthejeff@gmail.com" 1328 | }, 1329 | { 1330 | "name": "Sebastian Bergmann", 1331 | "email": "sebastian@phpunit.de" 1332 | }, 1333 | { 1334 | "name": "Adam Harvey", 1335 | "email": "aharvey@php.net" 1336 | } 1337 | ], 1338 | "description": "Provides functionality to recursively process PHP variables", 1339 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1340 | "time": "2015-06-21 08:04:50" 1341 | }, 1342 | { 1343 | "name": "sebastian/version", 1344 | "version": "1.0.6", 1345 | "source": { 1346 | "type": "git", 1347 | "url": "https://github.com/sebastianbergmann/version.git", 1348 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1349 | }, 1350 | "dist": { 1351 | "type": "zip", 1352 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1353 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1354 | "shasum": "" 1355 | }, 1356 | "type": "library", 1357 | "autoload": { 1358 | "classmap": [ 1359 | "src/" 1360 | ] 1361 | }, 1362 | "notification-url": "https://packagist.org/downloads/", 1363 | "license": [ 1364 | "BSD-3-Clause" 1365 | ], 1366 | "authors": [ 1367 | { 1368 | "name": "Sebastian Bergmann", 1369 | "email": "sebastian@phpunit.de", 1370 | "role": "lead" 1371 | } 1372 | ], 1373 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1374 | "homepage": "https://github.com/sebastianbergmann/version", 1375 | "time": "2015-06-21 13:59:46" 1376 | }, 1377 | { 1378 | "name": "symfony/event-dispatcher", 1379 | "version": "2.8.x-dev", 1380 | "source": { 1381 | "type": "git", 1382 | "url": "https://github.com/symfony/EventDispatcher.git", 1383 | "reference": "d7246885b7fe4cb5a2786bda34362d2f0e40b730" 1384 | }, 1385 | "dist": { 1386 | "type": "zip", 1387 | "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/d7246885b7fe4cb5a2786bda34362d2f0e40b730", 1388 | "reference": "d7246885b7fe4cb5a2786bda34362d2f0e40b730", 1389 | "shasum": "" 1390 | }, 1391 | "require": { 1392 | "php": ">=5.3.9" 1393 | }, 1394 | "require-dev": { 1395 | "psr/log": "~1.0", 1396 | "symfony/config": "~2.0,>=2.0.5|~3.0.0", 1397 | "symfony/dependency-injection": "~2.6|~3.0.0", 1398 | "symfony/expression-language": "~2.6|~3.0.0", 1399 | "symfony/phpunit-bridge": "~2.7|~3.0.0", 1400 | "symfony/stopwatch": "~2.3|~3.0.0" 1401 | }, 1402 | "suggest": { 1403 | "symfony/dependency-injection": "", 1404 | "symfony/http-kernel": "" 1405 | }, 1406 | "type": "library", 1407 | "extra": { 1408 | "branch-alias": { 1409 | "dev-master": "2.8-dev" 1410 | } 1411 | }, 1412 | "autoload": { 1413 | "psr-4": { 1414 | "Symfony\\Component\\EventDispatcher\\": "" 1415 | } 1416 | }, 1417 | "notification-url": "https://packagist.org/downloads/", 1418 | "license": [ 1419 | "MIT" 1420 | ], 1421 | "authors": [ 1422 | { 1423 | "name": "Fabien Potencier", 1424 | "email": "fabien@symfony.com" 1425 | }, 1426 | { 1427 | "name": "Symfony Community", 1428 | "homepage": "https://symfony.com/contributors" 1429 | } 1430 | ], 1431 | "description": "Symfony EventDispatcher Component", 1432 | "homepage": "https://symfony.com", 1433 | "time": "2015-06-24 15:32:32" 1434 | }, 1435 | { 1436 | "name": "symfony/filesystem", 1437 | "version": "2.8.x-dev", 1438 | "source": { 1439 | "type": "git", 1440 | "url": "https://github.com/symfony/Filesystem.git", 1441 | "reference": "9f70c5625a32b2f1e6fc37222f52b4e0eb437b0e" 1442 | }, 1443 | "dist": { 1444 | "type": "zip", 1445 | "url": "https://api.github.com/repos/symfony/Filesystem/zipball/9f70c5625a32b2f1e6fc37222f52b4e0eb437b0e", 1446 | "reference": "9f70c5625a32b2f1e6fc37222f52b4e0eb437b0e", 1447 | "shasum": "" 1448 | }, 1449 | "require": { 1450 | "php": ">=5.3.9" 1451 | }, 1452 | "require-dev": { 1453 | "symfony/phpunit-bridge": "~2.7|~3.0.0" 1454 | }, 1455 | "type": "library", 1456 | "extra": { 1457 | "branch-alias": { 1458 | "dev-master": "2.8-dev" 1459 | } 1460 | }, 1461 | "autoload": { 1462 | "psr-4": { 1463 | "Symfony\\Component\\Filesystem\\": "" 1464 | } 1465 | }, 1466 | "notification-url": "https://packagist.org/downloads/", 1467 | "license": [ 1468 | "MIT" 1469 | ], 1470 | "authors": [ 1471 | { 1472 | "name": "Fabien Potencier", 1473 | "email": "fabien@symfony.com" 1474 | }, 1475 | { 1476 | "name": "Symfony Community", 1477 | "homepage": "https://symfony.com/contributors" 1478 | } 1479 | ], 1480 | "description": "Symfony Filesystem Component", 1481 | "homepage": "https://symfony.com", 1482 | "time": "2015-07-09 16:11:14" 1483 | }, 1484 | { 1485 | "name": "symfony/stopwatch", 1486 | "version": "2.8.x-dev", 1487 | "source": { 1488 | "type": "git", 1489 | "url": "https://github.com/symfony/Stopwatch.git", 1490 | "reference": "cd5f0dc1d3d0e2c83461dad77e20a9186beb6146" 1491 | }, 1492 | "dist": { 1493 | "type": "zip", 1494 | "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/cd5f0dc1d3d0e2c83461dad77e20a9186beb6146", 1495 | "reference": "cd5f0dc1d3d0e2c83461dad77e20a9186beb6146", 1496 | "shasum": "" 1497 | }, 1498 | "require": { 1499 | "php": ">=5.3.9" 1500 | }, 1501 | "require-dev": { 1502 | "symfony/phpunit-bridge": "~2.7|~3.0.0" 1503 | }, 1504 | "type": "library", 1505 | "extra": { 1506 | "branch-alias": { 1507 | "dev-master": "2.8-dev" 1508 | } 1509 | }, 1510 | "autoload": { 1511 | "psr-4": { 1512 | "Symfony\\Component\\Stopwatch\\": "" 1513 | } 1514 | }, 1515 | "notification-url": "https://packagist.org/downloads/", 1516 | "license": [ 1517 | "MIT" 1518 | ], 1519 | "authors": [ 1520 | { 1521 | "name": "Fabien Potencier", 1522 | "email": "fabien@symfony.com" 1523 | }, 1524 | { 1525 | "name": "Symfony Community", 1526 | "homepage": "https://symfony.com/contributors" 1527 | } 1528 | ], 1529 | "description": "Symfony Stopwatch Component", 1530 | "homepage": "https://symfony.com", 1531 | "time": "2015-07-01 18:24:26" 1532 | }, 1533 | { 1534 | "name": "symfony/yaml", 1535 | "version": "dev-master", 1536 | "source": { 1537 | "type": "git", 1538 | "url": "https://github.com/symfony/Yaml.git", 1539 | "reference": "d067e47c2be88b62c3e50d1362d959efc46ffefc" 1540 | }, 1541 | "dist": { 1542 | "type": "zip", 1543 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/d067e47c2be88b62c3e50d1362d959efc46ffefc", 1544 | "reference": "d067e47c2be88b62c3e50d1362d959efc46ffefc", 1545 | "shasum": "" 1546 | }, 1547 | "require": { 1548 | "php": ">=5.5.9" 1549 | }, 1550 | "require-dev": { 1551 | "symfony/phpunit-bridge": "~2.8|~3.0" 1552 | }, 1553 | "type": "library", 1554 | "extra": { 1555 | "branch-alias": { 1556 | "dev-master": "3.0-dev" 1557 | } 1558 | }, 1559 | "autoload": { 1560 | "psr-4": { 1561 | "Symfony\\Component\\Yaml\\": "" 1562 | } 1563 | }, 1564 | "notification-url": "https://packagist.org/downloads/", 1565 | "license": [ 1566 | "MIT" 1567 | ], 1568 | "authors": [ 1569 | { 1570 | "name": "Fabien Potencier", 1571 | "email": "fabien@symfony.com" 1572 | }, 1573 | { 1574 | "name": "Symfony Community", 1575 | "homepage": "https://symfony.com/contributors" 1576 | } 1577 | ], 1578 | "description": "Symfony Yaml Component", 1579 | "homepage": "https://symfony.com", 1580 | "time": "2015-08-01 06:48:35" 1581 | } 1582 | ], 1583 | "aliases": [], 1584 | "minimum-stability": "dev", 1585 | "stability-flags": { 1586 | "paragonie/random_compat": 20, 1587 | "indigophp/hash-compat": 20 1588 | }, 1589 | "prefer-stable": false, 1590 | "prefer-lowest": false, 1591 | "platform": { 1592 | "php": ">=5.5" 1593 | }, 1594 | "platform-dev": [] 1595 | } 1596 | -------------------------------------------------------------------------------- /demo.php: -------------------------------------------------------------------------------- 1 | create("foobar", 3, 5, 4); 8 | 9 | var_dump($result); 10 | 11 | var_dump($hash->verify("foobar", $result)); 12 | 13 | 14 | var_dump($hash->verify("foobaz", $result)); 15 | -------------------------------------------------------------------------------- /lib/BallAndChain/Command/Build.php: -------------------------------------------------------------------------------- 1 | setName('build') 21 | ->setDescription('Build a seed file') 22 | ->addArgument('size', InputArgument::REQUIRED, 'The size of file to generate') 23 | ->addArgument('file', InputArgument::REQUIRED, 'The name of file to generate'); 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) { 27 | $f = fopen($input->getArgument('file'), 'w+'); 28 | $random = fopen('/dev/urandom', 'r'); 29 | $written = 0; 30 | $bytes = $this->parseSize($input->getArgument('size')); 31 | do { 32 | $output->writeln("$written bytes written"); 33 | $written += stream_copy_to_stream($random, $f, $bytes - $written); 34 | } while($written < $bytes); 35 | } 36 | 37 | protected function parseSize($size) { 38 | if (is_numeric($size)) { 39 | return (int) $size; 40 | } 41 | switch (strtoupper(substr($size, -1))) { 42 | case 'K': 43 | return 1024 * $this->parseSize(substr($size, 0, -1)); 44 | case 'M': 45 | return 1024 * 1024 * $this->parseSize(substr($size, 0, -1)); 46 | case 'G': 47 | return 1024 * 1024 * 1024 * $this->parseSize(substr($size, 0, -1)); 48 | case 'T': 49 | return 1024 * 1024 * 1024 * 1024 * $this->parseSize(substr($size, 0, -1)); 50 | } 51 | throw new \RuntimeException("Unknown size: $size"); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /lib/BallAndChain/Hash.php: -------------------------------------------------------------------------------- 1 | file = fopen($fileName, 'r'); 24 | if (!$this->file) { 25 | throw new \RuntimeException("Invalid seed file"); 26 | } 27 | $this->fileSize = (int) fstat($this->file)['size']; 28 | } 29 | 30 | public function create($password, $rounds = 3, $pointerSize = 0, $dataSize = 4) { 31 | if ($rounds > 62) { 32 | throw new \RuntimeException("Rounds setting is too high, it must be <= 62"); 33 | } elseif ($rounds <= 1) { 34 | throw new \RuntimeException("Rounds setting is too low, it must be >= 2"); 35 | } 36 | if ($dataSize > 62) { 37 | throw new \RuntimeException("Data size setting is too high, it must be <= 62"); 38 | } elseif ($dataSize <= 1) { 39 | throw new \RuntimeException("Data size setting is too low, it must be >= 2"); 40 | } 41 | $dataSizeDecoded = pow(2, $dataSize); 42 | $requiredPointer = ceil((log($this->fileSize) / log(2)) / 8); 43 | if ($pointerSize === 0) { 44 | // compute based on size of file 45 | $pointerSize = $requiredPointer; 46 | } elseif ($pointerSize < $requiredPointer) { 47 | throw new \RuntimeException("Pointer setting is too low, needs to be at least $requiredPointer bytes"); 48 | } 49 | $key = hash(self::HASH_PRIMITIVE, $password, true); 50 | $pointers = ''; 51 | $iterationCount = pow(2, $rounds); 52 | $h = hash_init(self::HASH_PRIMITIVE); 53 | for ($i = 0; $i < $iterationCount; $i++) { 54 | $pointers .= $pointer = random_bytes($pointerSize); 55 | hash_update($h, $this->read($pointer, $dataSizeDecoded)); 56 | } 57 | $result = $pointers . hash_final($h, true); 58 | $iv = random_bytes(self::IV_LENGTH); 59 | $header = pack('CCCC', 1, $rounds, $pointerSize, $dataSize); 60 | return base64_encode($header . $iv . openssl_encrypt($result, self::CIPHER_PRIMITIVE, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv)); 61 | } 62 | 63 | public function verify($password, $hash) { 64 | $key = hash(self::HASH_PRIMITIVE, $password, true); 65 | $hash = base64_decode($hash); 66 | $header = substr($hash, 0, self::HEADER_SIZE); 67 | $iv = substr($hash, self::HEADER_SIZE, self::IV_LENGTH); 68 | $ciphertext = substr($hash, self::HEADER_SIZE + self::IV_LENGTH); 69 | $decrypted = openssl_decrypt($ciphertext, self::CIPHER_PRIMITIVE, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); 70 | list (, $version, $rounds, $pointerSize, $dataSize) = unpack('C*', $header); 71 | $iterationCount = pow(2, $rounds); 72 | $dataSizeDecoded = pow(2, $dataSize); 73 | if ($version !== 1) { 74 | throw new \RuntimeException("Unknown version encountered"); 75 | } 76 | if (strlen($decrypted) !== self::HASH_LENGTH + $iterationCount * $pointerSize) { 77 | throw new \RuntimeException("Invalid data payload, was it truncated?"); 78 | } 79 | $h = hash_init(self::HASH_PRIMITIVE); 80 | for ($i = 0; $i < $iterationCount; $i++) { 81 | $pointer = substr($decrypted, $i * $pointerSize, $pointerSize); 82 | hash_update($h, $this->read($pointer, $dataSizeDecoded)); 83 | } 84 | $test = hash_final($h, true); 85 | return hash_equals($test, substr($decrypted, $iterationCount * $pointerSize)); 86 | } 87 | 88 | protected function read($pointer, $dataSize) { 89 | $numeric = abs(unpack('q', str_pad($pointer, 8, chr(0), STR_PAD_LEFT))[1]); 90 | $offset = $numeric % $this->fileSize; 91 | fseek($this->file, $offset); 92 | $data = fread($this->file, $dataSize); 93 | if ($offset + $dataSize > $this->fileSize) { 94 | // wrap the read 95 | fseek($this->file, 0); 96 | $data .= fread($this->file, $dataSize - ($this->fileSize - $offset)); 97 | } 98 | if (strlen($data) !== $dataSize) { 99 | throw new \RuntimeException("Invalid data read, couldn't fill data block"); 100 | } 101 | return $data; 102 | } 103 | } --------------------------------------------------------------------------------