Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Add Argon2i for password hashing #58

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open

Add Argon2i for password hashing #58

wants to merge 2 commits into from

Conversation

MatthiasKuehneEllerhold
Copy link

@MatthiasKuehneEllerhold MatthiasKuehneEllerhold commented May 8, 2018

This PR adds an extra class for the argon2i password hash algorithm introduced with PHP7.2.

There are some open questions with this:

  1. Do we really want to have 1 new class for each algorithm PHP addes? "Argon2id" is just around the corner...

  2. PHP's password_verify() accepts currently both Bcrypt and Argon2i hashes. So basically you could either use the Bcrypt class or the Argon2i class to verify either hashes.
    This makes it pretty easy to migrate users from Bcrypt to Argon2i:

  • Use the new Argon2i class as the dependency
  • old bcrypt passwords are still verified correctly
  • new password will be created with argon2i
  1. Do we need a wrapper function for password_needs_rehash() ? This would mean we need some kind of inter-class upgrade path (from class Bcrypt to Argon2i in future php-versions).
    Although we dont know WHY password_needs_rehash() returns false: is it because of the algorithm or the cost value(s)?

  2. This class is marked as PHP7.2+ only (it throws an exception in the constructor). Do we want to provide fallbacks of some kind for older PHP Versions?

  3. Other than the algorithm no other PHP7.2+ specific features were used in this class (e. g. scalar type hints and return types) because a syntax error is much more heavy and harder to catch than a constructor-exception (Pre 7.0).
    Should type hints and return types get added because its a PHP7.2+ class anyway?

@MatthiasKuehneEllerhold
Copy link
Author

I've refactored out the usage of the default constants but it seems like the PHP7.2 binaries from travis-ci have no argon2 support whatsoever, see: travis-ci/travis-ci#8863

Copy link
Member

@Ocramius Ocramius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthiasKuehneEllerhold please base this on top of develop, which is where new features should land 👍

);
}

if (! empty($options)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverse the conditional to return early

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* @param array|Traversable $options
* @throws Exception\InvalidArgumentException
*/
public function __construct($options = [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ezimuel can we get rid of this kind of un-typed ctors?

*
* @return Argon2i
*/
public function setMemoryCost($memoryCost)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't expose any of these setter/getters anymore

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ocramius I think we still need this in order to be compliant with the previous implementations like Zend\Crypt\Password\Bcrypt, Zend\Crypt\Password\BcryptSha and Zend\Crypt\Password\Apache.
Even, because we can add some check on setter to verify that the parameter is correct with a detailed error message if not. IMHO, this is actually one of the advantages to use a wrapper class like Argon2i instead of the PHP bult-in function.

@MatthiasKuehneEllerhold MatthiasKuehneEllerhold changed the base branch from master to develop May 9, 2018 11:22
@MatthiasKuehneEllerhold
Copy link
Author

Rebased it unto develop and changed the PR target.

I've tried to orient myself on the existing bcrypt class.

@Ocramius
Copy link
Member

Ocramius commented May 9, 2018

I've tried to orient myself on the existing bcrypt class.

No worries, I'm just suggesting to get rid of some cruft, especially on new classes. @ezimuel needs to check these, since he's the lead here.

Copy link
Contributor

@ezimuel ezimuel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthiasKuehneEllerhold thanks for this PR and sorry for the delay in reviewing. I provide my feedbakcs, please check and let me know. Thanks again for your great work!

*
* @return Argon2i
*/
public function setMemoryCost($memoryCost)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ocramius I think we still need this in order to be compliant with the previous implementations like Zend\Crypt\Password\Bcrypt, Zend\Crypt\Password\BcryptSha and Zend\Crypt\Password\Apache.
Even, because we can add some check on setter to verify that the parameter is correct with a detailed error message if not. IMHO, this is actually one of the advantages to use a wrapper class like Argon2i instead of the PHP bult-in function.

*
* @var int|null
*/
protected $memoryCost;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will suggest to assign the default PHP values for $memoryCost, $timeCost and $threads. Using the constants:

PASSWORD_ARGON2_DEFAULT_MEMORY_COST
PASSWORD_ARGON2_DEFAULT_TIME_COST
PASSWORD_ARGON2_DEFAULT_THREADS

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the constants on PHP < 7.2 leads to an "Undefined constant" error even if you just autoload the class. (See the <7.2 Unit-Tests).

{
$options = [];

if ($this->memoryCost !== null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the default PHP values, we can omit this check with null values and always pass the $options to password_hash.

*
* @return Argon2i
*/
public function setTimeCost($timeCost)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check the value of $timeCost and verify that is allowed values for $options in password_hash(). As commented before, we should offer a safe way to use the Argon2i parameter with a detailed message in case of invalid values.

@ezimuel
Copy link
Contributor

ezimuel commented Jul 12, 2018

@MatthiasKuehneEllerhold I forgot to answer to your main questions:

  1. yes, this is the actual architecture of zend-crypt. Maybe, we will change it in the future.
  2. yes, you right the password_verify is used by Bcrypt and Argon2i but this is ok and actually good for portability purpose, as you already noticed.
  3. no, I don't think we need a wrapper for password_needs_rehash() function of PHP. The usage of this function is simple and people can use directly it. We don't see any advantages for a wrapper class here.
  4. no, I don't think we need to support Argon2i for PHP < 7.2. I implemented, years ago, Scrypt directly in PHP but the performance are not so good as C, of course.
  5. no, at the moment we don't use type hint for zend-crypt for backwards compatibility, we are still supporting PHP 5.6. That said, we will change it soon with a new major release.

@weierophinney
Copy link
Member

This repository has been closed and moved to laminas/laminas-crypt; a new issue has been opened at laminas/laminas-crypt#1.

@weierophinney
Copy link
Member

This repository has been moved to laminas/laminas-crypt. If you feel that this patch is still relevant, please re-open against that repository, and reference this issue. To re-open, we suggest the following workflow:

  • Squash all commits in your branch (git rebase -i origin/{branch})
  • Make a note of all changed files (`git diff --name-only origin/{branch}...HEAD
  • Run the laminas/laminas-migration tool on the code.
  • Clone laminas/laminas-crypt to another directory.
  • Copy the files from the second bullet point to the clone of laminas/laminas-crypt.
  • In your clone of laminas/laminas-crypt, commit the files, push to your fork, and open the new PR.
    We will be providing tooling via laminas/laminas-migration soon to help automate the process.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants