Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 4.0 #15

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .php_cs.cache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"php":"7.2.12","version":"2.13.1:v2.13.1#54814c62d5beef3ba55297b9b3186ed8b8a1b161","rules":{"encoding":true,"full_opening_tag":true,"blank_line_after_namespace":true,"braces":true,"class_definition":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_constants":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"psr4":true,"declare_strict_types":true,"strict_param":true,"align_multiline_comment":true,"array_syntax":{"syntax":"short"},"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_before_statement":{"statements":["while","declare","do","for","foreach","if","switch","try"]},"cast_spaces":true,"class_attributes_separation":true,"combine_consecutive_issets":true,"combine_consecutive_unsets":true,"compact_nullable_typehint":true,"concat_space":{"spacing":"one"},"declare_equal_normalize":true,"dir_constant":true,"ereg_to_preg":true,"escape_implicit_backslashes":true,"is_null":{"use_yoda_style":true},"linebreak_after_opening_tag":true,"list_syntax":{"syntax":"short"},"lowercase_cast":true,"magic_constant_casing":true,"method_chaining_indentation":true,"method_separation":true,"modernize_types_casting":true,"no_alias_functions":true,"no_blank_lines_after_class_opening":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_consecutive_blank_lines":{"tokens":["break","continue","extra","return","throw","use","parenthesis_brace_block","square_brace_block","curly_brace_block"]},"no_homoglyph_names":true,"no_leading_import_slash":true,"no_php4_constructor":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":true,"no_unneeded_curly_braces":true,"no_unneeded_final_method":true,"no_unreachable_default_argument_value":true,"no_unused_imports":true,"no_useless_else":true,"no_useless_return":true,"no_whitespace_before_comma_in_array":true,"no_whitespace_in_blank_line":true,"normalize_index_brace":true,"return_type_declaration":true,"trim_array_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":{"equal":true,"identical":true,"always_move_variable":true},"ordered_imports":true,"ordered_class_elements":true,"no_superfluous_elseif":true,"no_short_echo_tag":true,"no_null_property_initialization":true,"blank_line_before_return":true,"heredoc_to_nowdoc":true,"phpdoc_align":{"tags":["param","return","throws","type","var"]},"phpdoc_to_comment":true},"hashes":{"src\/Exception\/HashingException.php":2572493559,"src\/PasswordLock.php":180371002,"src\/Hasher\/PasswordHasherInterface.php":3091791932,"src\/Hasher\/PasswordHasher.php":3257075415,"tests\/PasswordLockTest.php":3624153454,"tests\/Hasher\/PasswordHasherTest.php":824329663}}
107 changes: 107 additions & 0 deletions .php_cs.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

return PhpCsFixer\Config::create()
->setRules([
'@PSR1' => true,
'@PSR2' => true,
'psr4' => true,
'declare_strict_types' => true,
'strict_param' => true,
'strict_comparison' => false,
'align_multiline_comment' => true,
'array_syntax' => ['syntax' => 'short'],
'binary_operator_spaces' => true,
'blank_line_after_opening_tag' => true,
'blank_line_before_statement' => [
'statements' => [
'while', 'declare', 'do', 'for', 'foreach', 'if', 'switch', 'try'
]
],
'cast_spaces' => true,
'class_attributes_separation' => true,
'combine_consecutive_issets' => true,
'combine_consecutive_unsets' => true,
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => true,
'dir_constant' => true,
'ereg_to_preg' => true,
'escape_implicit_backslashes' => true,
'is_null' => ['use_yoda_style' => true],
'linebreak_after_opening_tag' => true,
'list_syntax' => ['syntax' => 'short'],
'lowercase_cast' => true,
'magic_constant_casing' => true,
'method_chaining_indentation' => true,
'method_separation' => true,
'modernize_types_casting' => true,
'no_alias_functions' => true,
'no_blank_lines_after_class_opening' => true,
'no_blank_lines_after_phpdoc' => true,
'no_empty_comment' => true,
'no_empty_phpdoc' => true,
'no_empty_statement' => true,
'no_extra_consecutive_blank_lines' => [
'tokens' => [
'break', 'continue', 'extra', 'return', 'throw', 'use',
'parenthesis_brace_block', 'square_brace_block', 'curly_brace_block'
],
],
'no_homoglyph_names' => true,
'no_leading_import_slash' => true,
'no_php4_constructor' => true,
'no_short_bool_cast' => true,
'no_singleline_whitespace_before_semicolons' => true,
'no_spaces_around_offset' => true,
'no_trailing_comma_in_list_call' => true,
'no_trailing_comma_in_singleline_array' => true,
'no_unneeded_control_parentheses' => true,
'no_unneeded_curly_braces' => true,
'no_unneeded_final_method' => true,
'no_unreachable_default_argument_value' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'no_whitespace_before_comma_in_array' => true,
'no_whitespace_in_blank_line' => true,
'normalize_index_brace' => true,
'return_type_declaration' => true,
'trim_array_spaces' => true,
'unary_operator_spaces' => true,
'whitespace_after_comma_in_array' => true,
'yoda_style' => [
'equal' => true,
'identical' => true,
'always_move_variable' => true
],
'ordered_imports' => true,
'ordered_class_elements' => true,
'no_superfluous_elseif' => true,
'no_short_echo_tag' => true,
'no_null_property_initialization' => true,
'no_closing_tag' => true,
'blank_line_before_return' => true,
'class_keyword_remove' => false,
'self_accessor' => false,
'encoding' => true,
'full_opening_tag' => true,
'heredoc_to_nowdoc' => true,
'mb_str_functions' => false,
'phpdoc_align' => [
'tags' => [
'param', 'return', 'throws', 'type', 'var'
],
],
'phpdoc_to_comment' => true,
'no_trailing_whitespace' => true,
])
->setRiskyAllowed(true)
->setUsingCache(true)
->setHideProgress(false)
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__.'/src')
->name('*.php')
);
29 changes: 22 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
language: php
sudo: false

php:
- 7.0
- 7.1
- 7.2

matrix:
fast_finish: true
include:
- php: 7.1
env:
- DEPS=lowest
- php: 7.1
env:
- CS_CHECK=true
- ANALYZE=true
- php: 7.2
- php: 7.3
env:
- NO_CS=true
- php: nightly
env:
- NO_CS=true
allow_failures:
- php: 7.3
- php: nightly

install:
- composer self-update
- if [[ $NO_CS == 'true' ]]; then composer remove --dev friendsofphp/php-cs-fixer ; fi
- composer update
- chmod +x ./run-tests.sh

script:
- vendor/bin/phpunit
- vendor/bin/psalm
- composer test
- if [[ $ANALYZE == 'true' ]]; then composer analyze ; fi
- if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi
99 changes: 84 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**MIT Licensed** - feel free to use to enhance the security of any of your PHP projects

Wraps Bcrypt-SHA384 in Authenticated Encryption. Published by [Paragon Initiative Enteprises](https://paragonie.com). Check out our other [open source projects](https://paragonie.com/projects) too.
Wraps Password Hashing in Authenticated Encryption. Published by [Paragon Initiative Enteprises](https://paragonie.com). Check out our other [open source projects](https://paragonie.com/projects) too.

Depends on [defuse/php-encryption](https://github.com/defuse/php-encryption) for authenticated symmetric-key encryption.

Expand All @@ -16,7 +16,7 @@ A hash then encrypt strategy offers **agility**; if your secret key is compromis

* You don't have to worry about the 72 character limit for bcrypt
* You don't have to worry about accidentally creating a null-byte truncation vulnerability
* If your database gets hacked, and your database is on a separate machine from your webserver, the attacker has to first decrypt the hashes before attempting to crack any of them.
* If your database gets hacked, and your database is on a separate machine from your web server, the attacker has to first decrypt the hashes before attempting to crack any of them.

Here's a [proof-of-concept](http://3v4l.org/61VZq) for the first two points.

Expand All @@ -27,26 +27,37 @@ But realistically, this library is only about as a secure as bcrypt.
### Hash Password, Encrypt Hash, Authenticate Ciphertext

```php
use \ParagonIE\PasswordLock\PasswordLock;
use \Defuse\Crypto\Key;
<?php

use ParagonIE\PasswordLock\PasswordLock;
use Defuse\Crypto\Key;

$key = Key::createNewRandomKey();

$passwordLock = new PasswordLock($key);

if (isset($_POST['password'])) {
if (!is_string($_POST['password'])) {
die("Password must be a string");
die('Password must be a string');
}
$storeMe = PasswordLock::hashAndEncrypt($_POST['password'], $key);

$storeMe = $passwordLock->lock($_POST['password']);
}
```

### Verify MAC, Decrypt Ciphertext, Verify Password

```php
<?php

...

if (isset($_POST['password'])) {
if (!is_string($_POST['password'])) {
die("Password must be a string");
die('Password must be a string');
}
if (PasswordLock::decryptAndVerify($_POST['password'], $storeMe, $key)) {

if ($passwordLock->check($_POST['password'], $storeMe)) {
// Success!
}
}
Expand All @@ -55,17 +66,75 @@ if (isset($_POST['password'])) {
### Re-encrypt a hash with a different encryption key

```php
<?php

use ParagonIE\PasswordLock\PasswordLock;

$newKey = \Defuse\Crypto\Key::createNewRandomKey();
$newHash = PasswordLock::rotateKey($storeMe, $key, $newKey);
```

### Migrate from Version 1 of the library
### Using Password hasher

by default, PasswordLock uses Bcrypt-SHA384 based PasswordHasher.

```php
$newHash = PasswordLock::upgradeFromVersion1(
$_POST['password'],
$oldHash,
$oldKey,
$newKey
);
<?php

use ParagonIE\PasswordLock\{
PasswordLock,
Hasher\PasswordHasher
};

// doing this :
$hasher = new PasswordHasher(PASSWORD_DEFAULT, []);
$lock = new PasswordLock($key,$hasher);
// is same as this :
$lock = new PasswordLock($key);
```

you can add options or specify another PHP `password_hash` algorithm as following :

```php
<?php

use ParagonIE\PasswordLock\{
PasswordLock,
Hasher\PasswordHasher
};

// use Argon2I algorithm instead of Bcrypt
$hasher = new PasswordHasher(PASSWORD_ARGON2I, [
'memory_cost' => 2048
]);
$lock = new PasswordLock($key,$hasher);
```

## Costume Password Hasher

`ParagonIE\PasswordLock\PasswordLock` accepts any `ParagonIE\PasswordLock\Hasher\PasswordHasherInterface` implementation as the first argument.

```php
<?php

use ParagonIE\PasswordLock\{
PasswordLock,
Hasher\PasswordHasherInterface
};

class MyPasswordHasher implements PasswordHasherInterface
{
public function hash(string $password) : string
{
// hash password
}

public function verify(string $password, string $hash) : bool
{
// verify hash against the given password
}
}

$hasher = new MyPasswordHasher();
$lock = new PasswordLock($key,$hasher);
```
73 changes: 45 additions & 28 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,39 +1,56 @@
{
"name": "paragonie/password_lock",
"description": "Wraps Bcrypt-SHA2 in Authenticated Encryption",
"name": "paragonie/password_lock",
"type": "library",
"description": "Wraps Password hashing in Authenticated Encryption",
"keywords": [
"password",
"hash",
"encryption",
"hashing"
],
"license": "MIT",
"type": "library",
"password",
"hash",
"encryption",
"hashing"
],
"license": "MIT",
"authors": [
{
"name": "Scott Arciszewski",
"email": "[email protected]",
"homepage": "https://paragonie.com",
"role": "Developer"
}
],
"support": {
"issues": "https://github.com/paragonie/password_lock/issues",
"email": "[email protected]",
"source": "https://github.com/paragonie/password_lock"
},
"autoload": {
"files": [
"src/PasswordLock.php"
]
},
{
"name": "Scott Arciszewski",
"email": "[email protected]",
"homepage": "https://paragonie.com",
"role": "Developer"
}
],
"require": {
"php": "^7",
"php": "^7.1",
"defuse/php-encryption": "^2",
"paragonie/constant_time_encoding": "^2"
},
"require-dev": {
"phpunit/phpunit": "^6",
"vimeo/psalm": "^0|^1"
"vimeo/psalm": "^0 || ^1",
"friendsofphp/php-cs-fixer": "^2.13"
},
"autoload": {
"psr-4": {
"ParagonIE\\PasswordLock\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"ParagonIE\\PasswordLock\\Tests\\": "tests/"
}
},
"scripts": {
"test": "phpunit --colors=always",
"analyze": "psalm",
"cs-check": "php-cs-fixer fix --dry-run -vvv",
"cs-fix": "php-cs-fixer fix -vvv",
"check": [
"@cs-check",
"@analyze",
"@test"
]
},
"support": {
"email": "[email protected]",
"issues": "https://github.com/paragonie/password_lock/issues",
"source": "https://github.com/paragonie/password_lock"
}
}
11 changes: 11 additions & 0 deletions src/Exception/HashingException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace ParagonIE\PasswordLock\Exception;

use RuntimeException;

class HashingException extends RuntimeException
{
}
Loading