diff --git a/CHANGELOG.md b/CHANGELOG.md index 2684f26..8937916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Whirlpool hash algorithm - 'Prepend salt' toggle +- Drupal 7 hash algorithm ### Fixed - Error when 'Display name' not set - Encoding of iteration for 'Extended DES (Crypt)' diff --git a/README.md b/README.md index 8bccbcf..f99d4fb 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,7 @@ MD5 (Crypt) | | $1$RzaFbNcU$u9adfTY/Q6za6nu0Ogrl1/ SHA256 (Crypt) | Generates hash with 5000 rounds. | $5$rounds=5000$VIYD0iHkg7uY9SRc$v2XLS/9dvfFN84mzGvW9wxnVt9Xd/urXaaTkpW8EwD1 SHA512 (Crypt) | Generates hash with 5000 rounds. | $6$rounds=5000$yH.Q0OL4qbCOUJ3q$Xry5EVFva3wKnfo8/ktrugmBd8tcl34NK6rXInv1HhmdSUNLEm0La9JnA57rqwQ.9/Bz513MD4tvmmISLUIHs/ Standard DES (Crypt) | | yTBnb7ab/N072 +Drupal 7 | See [phpass](http://www.openwall.com/phpass/). | $S$DC7eCpJQ3SUQtW4Bp.vKb2rpeaffi4iqk9OpYwJyEoSMsezn67Sl Joomla MD5 Encryption | Generates 32 chars salt. | 14d21b49b0f13e2acba962b6b0039edd:haJK0yTvBXTNMh76xwEw5RYEVpJsN8us MD5 | No salt supported. | 5f4dcc3b5aa765d61d8327deb882cf99 Portable PHP password | See [phpass](http://www.openwall.com/phpass/). | $P$BxrwraqNTi4as0EI.IpiA/K.muk9ke/ diff --git a/lib/Crypto/Drupal7.php b/lib/Crypto/Drupal7.php index 5b32a91..567e342 100644 --- a/lib/Crypto/Drupal7.php +++ b/lib/Crypto/Drupal7.php @@ -21,54 +21,34 @@ namespace OCA\UserSQL\Crypto; -use OCP\IL10N; - -require_once "Phpass.php"; - /** * Drupal 7 overrides of phpass hash implementation. * * @author BrandonKerr + * @author Marcin Łojewski */ class Drupal7 extends Phpass { - - /** - * The expected (and maximum) number of characters in a hashed password. - */ - const DRUPAL_HASH_LENGTH = 55; + /** + * The expected (and maximum) number of characters in a hashed password. + */ + const DRUPAL_HASH_LENGTH = 55; /** - * @param string $password Password to encrypt. - * @param string $setting Hash settings. - * - * @return string|null Generated hash. Null on invalid settings. + * @inheritdoc */ - private function crypt($password, $setting) + protected function crypt($password, $setting) { - $countLog2 = strpos(self::ITOA64, $setting[3]); - if ($countLog2 < 7 || $countLog2 > 30) { - return null; - } - - $count = 1 << $countLog2; - - $salt = substr($setting, 4, 8); - if (strlen($salt) !== 8) { - return null; - } - - $hash = hash('sha512', $salt . $password, true); - do { - $hash = hash('sha512', $hash . $password, true); - } while (--$count); - - $output = substr($setting, 0, 12); - $output .= $this->encode64($hash, strlen($hash)); - - return substr($output, 0, self::DRUPAL_HASH_LENGTH); + return substr(parent::crypt($password, $setting), 0, self::DRUPAL_HASH_LENGTH); } + /** + * @inheritdoc + */ + protected function hash($input) + { + return hash('sha512', $input, true); + } /** * @inheritdoc diff --git a/lib/Crypto/Phpass.php b/lib/Crypto/Phpass.php index d193917..a430ae3 100644 --- a/lib/Crypto/Phpass.php +++ b/lib/Crypto/Phpass.php @@ -61,7 +61,7 @@ class Phpass extends AbstractAlgorithm * * @return string|null Generated hash. Null on invalid settings. */ - private function crypt($password, $setting) + protected function crypt($password, $setting) { $countLog2 = strpos(self::ITOA64, $setting[3]); if ($countLog2 < 7 || $countLog2 > 30) { @@ -75,17 +75,29 @@ class Phpass extends AbstractAlgorithm return null; } - $hash = md5($salt . $password, true); + $hash = $this->hash($salt . $password); do { - $hash = md5($hash . $password, true); + $hash = $this->hash($hash . $password); } while (--$count); $output = substr($setting, 0, 12); - $output .= $this->encode64($hash, 16); + $output .= $this->encode64($hash, strlen($hash)); return $output; } + /** + * Apply hash function to input. + * + * @param string $input Input string. + * + * @return string Hashed input. + */ + protected function hash($input) + { + return md5($input, true); + } + /** * Encode binary input to base64 string. * diff --git a/tests/Crypto/Drupal7Test.php b/tests/Crypto/Drupal7Test.php new file mode 100644 index 0000000..834fcff --- /dev/null +++ b/tests/Crypto/Drupal7Test.php @@ -0,0 +1,61 @@ + + * @author Marcin Łojewski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Tests\UserSQL\Crypto; + +use OCA\UserSQL\Crypto\Drupal7; +use OCA\UserSQL\Crypto\IPasswordAlgorithm; +use OCP\IL10N; +use Test\TestCase; + +/** + * Unit tests for class Drupal7. + * + * @author Marcin Łojewski + */ +class Drupal7Test extends TestCase +{ + /** + * @var IPasswordAlgorithm + */ + private $crypto; + + public function testCheckPassword() + { + $this->assertTrue( + $this->crypto->checkPassword( + "password", "\$S\$DC7eCpJQ3SUQtW4Bp.vKb2rpeaffi4iqk9OpYwJyEoSMsezn67Sl" + ) + ); + } + + public function testPasswordHash() + { + $hash = $this->crypto->getPasswordHash("password"); + $this->assertTrue($this->crypto->checkPassword("password", $hash)); + } + + protected function setUp() + { + parent::setUp(); + $this->crypto = new Drupal7($this->createMock(IL10N::class)); + } +}