diff --git a/CHANGELOG.md b/CHANGELOG.md index 5018286..fe7f8c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Allow email login option - UID user table column - GID user table column +- HMAC hash implementation ## [4.4.1] - 2020-02-02 ### Fixed diff --git a/README.md b/README.md index c71f1cd..5d7b1ac 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,7 @@ Argon2i (Crypt) | Requires PHP >= 7.2. See [password_hash](http://php.net/manual Argon2id (Crypt) | Requires PHP >= 7.2. See [password_hash](http://php.net/manual/en/function.password-hash.php). | $argon2id$v=19$m=65536,t=4,p=1$eWhTd3huemlhNGFkWTVSSQ$BjSh9PINc9df9WU1zppBsYJKvkwUEYHYNUUMTj+QGPw Blowfish (Crypt) | See [password_hash](http://php.net/manual/en/function.password-hash.php). | $2y$10$5rsN1fmoSkaRy9bqhozAXOr0mn0QiVIfd2L04Bbk1Go9MjdvotwBq Extended DES (Crypt) | | cDRpdxPmHpzS. +Hash HMAC | See [hash_hmac](https://www.php.net/manual/en/function.hash-hmac.php). | ba4f8624f0a4d1f2a3991f4d88cd9afb604dac20 MD5 (Crypt) | | $1$RzaFbNcU$u9adfTY/Q6za6nu0Ogrl1/ SHA256 (Crypt) | | $5$rounds=5000$VIYD0iHkg7uY9SRc$v2XLS/9dvfFN84mzGvW9wxnVt9Xd/urXaaTkpW8EwD1 SHA512 (Crypt) | | $6$rounds=5000$yH.Q0OL4qbCOUJ3q$Xry5EVFva3wKnfo8/ktrugmBd8tcl34NK6rXInv1HhmdSUNLEm0La9JnA57rqwQ.9/Bz513MD4tvmmISLUIHs/ diff --git a/js/settings.js b/js/settings.js index 18a3d4d..07c3e41 100644 --- a/js/settings.js +++ b/js/settings.js @@ -76,15 +76,41 @@ user_sql.adminSettingsUI = function () { var param = $("
"); var label = $("").attr({for: "opt-crypto_param_" + index}); var title = $("").text(data.data[index]["name"]); - var input = $("").attr({ - type: "number", - id: "opt-crypto_param_" + index, - name: "opt-crypto_param_" + index, - step: 1, - min: data.data[index]["min"], - max: data.data[index]["max"], - value: data.data[index]["value"] - }); + + var input = null; + switch (data.data[index]["type"]) { + case "choice": + input = $("").attr({ + type: "number", + id: "opt-crypto_param_" + index, + name: "opt-crypto_param_" + index, + step: 1, + min: data.data[index]["min"], + max: data.data[index]["max"], + value: data.data[index]["value"] + }); + break; + default: + break; + } label.append(title); param.append(label); diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 3d06b6b..6d83920 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -31,6 +31,8 @@ use OCA\UserSQL\Constant\App; use OCA\UserSQL\Constant\DB; use OCA\UserSQL\Constant\Opt; use OCA\UserSQL\Crypto\IPasswordAlgorithm; +use OCA\UserSQL\Crypto\Param\ChoiceParam; +use OCA\UserSQL\Crypto\Param\IntParam; use OCA\UserSQL\Platform\PlatformFactory; use OCA\UserSQL\Properties; use OCP\AppFramework\Controller; @@ -77,8 +79,7 @@ class SettingsController extends Controller public function __construct( $appName, IRequest $request, ILogger $logger, IL10N $localization, Properties $properties, Cache $cache - ) - { + ) { parent::__construct($appName, $request); $this->appName = $appName; $this->logger = $logger; @@ -267,13 +268,23 @@ class SettingsController extends Controller $reqParam = $this->request->getParam( "opt-crypto_param_" . $i, null ); - $cryptoParam = $configuration[$i]; - - if (is_null($reqParam) || $reqParam < $cryptoParam->min - || $reqParam > $cryptoParam->max - ) { + if (is_null($reqParam)) { return false; } + + $cryptoParam = $configuration[$i]; + switch ($cryptoParam->type) { + case ChoiceParam::TYPE: + if (!in_array($reqParam, $cryptoParam->choices)) { + return false; + } + break; + case IntParam::TYPE: + if ($reqParam < $cryptoParam->min || $reqParam > $cryptoParam->max) { + return false; + } + break; + } } return true; diff --git a/lib/Crypto/CryptArgon2.php b/lib/Crypto/CryptArgon2.php index ef41cee..60b7ec9 100644 --- a/lib/Crypto/CryptArgon2.php +++ b/lib/Crypto/CryptArgon2.php @@ -21,6 +21,7 @@ namespace OCA\UserSQL\Crypto; +use OCA\UserSQL\Crypto\Param\IntParam; use OCP\IL10N; /** @@ -106,14 +107,14 @@ class CryptArgon2 extends AbstractAlgorithm public function configuration() { return [ - new CryptoParam( + new IntParam( "Memory cost (KiB)", PASSWORD_ARGON2_DEFAULT_MEMORY_COST, 1, 1048576 ), - new CryptoParam( + new IntParam( "Time cost", PASSWORD_ARGON2_DEFAULT_TIME_COST, 1, 1024 ), - new CryptoParam("Threads", PASSWORD_ARGON2_DEFAULT_THREADS, 1, 1024) + new IntParam("Threads", PASSWORD_ARGON2_DEFAULT_THREADS, 1, 1024) ]; } diff --git a/lib/Crypto/CryptArgon2id.php b/lib/Crypto/CryptArgon2id.php index 77f610b..efe4475 100644 --- a/lib/Crypto/CryptArgon2id.php +++ b/lib/Crypto/CryptArgon2id.php @@ -21,6 +21,7 @@ namespace OCA\UserSQL\Crypto; +use OCA\UserSQL\Crypto\Param\IntParam; use OCP\IL10N; /** @@ -92,7 +93,7 @@ class CryptArgon2id extends AbstractAlgorithm public function getPasswordHash($password, $salt = null) { return password_hash( - $password, PASSWORD_ARGON2ID, [ + $password, PASSWORD_ARGON2ID, [ "memory_cost" => $this->memoryCost, "time_cost" => $this->timeCost, "threads" => $this->threads @@ -106,14 +107,14 @@ class CryptArgon2id extends AbstractAlgorithm public function configuration() { return [ - new CryptoParam( + new IntParam( "Memory cost (KiB)", PASSWORD_ARGON2_DEFAULT_MEMORY_COST, 1, 1048576 ), - new CryptoParam( + new IntParam( "Time cost", PASSWORD_ARGON2_DEFAULT_TIME_COST, 1, 1024 ), - new CryptoParam("Threads", PASSWORD_ARGON2_DEFAULT_THREADS, 1, 1024) + new IntParam("Threads", PASSWORD_ARGON2_DEFAULT_THREADS, 1, 1024) ]; } diff --git a/lib/Crypto/CryptBlowfish.php b/lib/Crypto/CryptBlowfish.php index fb9e28b..103ee73 100644 --- a/lib/Crypto/CryptBlowfish.php +++ b/lib/Crypto/CryptBlowfish.php @@ -21,6 +21,7 @@ namespace OCA\UserSQL\Crypto; +use OCA\UserSQL\Crypto\Param\IntParam; use OCP\IL10N; /** @@ -72,7 +73,7 @@ class CryptBlowfish extends AbstractAlgorithm */ public function configuration() { - return [new CryptoParam("Cost", 10, 4, 31)]; + return [new IntParam("Cost", 10, 4, 31)]; } /** diff --git a/lib/Crypto/CryptExtendedDES.php b/lib/Crypto/CryptExtendedDES.php index 8cde30a..f59b778 100644 --- a/lib/Crypto/CryptExtendedDES.php +++ b/lib/Crypto/CryptExtendedDES.php @@ -21,6 +21,7 @@ namespace OCA\UserSQL\Crypto; +use OCA\UserSQL\Crypto\Param\IntParam; use OCP\IL10N; /** @@ -53,7 +54,7 @@ class CryptExtendedDES extends AbstractCrypt */ public function configuration() { - return [new CryptoParam("Iterations", 1000, 0, 16777215)]; + return [new IntParam("Iterations", 1000, 0, 16777215)]; } /** diff --git a/lib/Crypto/CryptSHA256.php b/lib/Crypto/CryptSHA256.php index 91063ef..f6a3c9a 100644 --- a/lib/Crypto/CryptSHA256.php +++ b/lib/Crypto/CryptSHA256.php @@ -21,6 +21,7 @@ namespace OCA\UserSQL\Crypto; +use OCA\UserSQL\Crypto\Param\IntParam; use OCP\IL10N; /** @@ -40,7 +41,7 @@ class CryptSHA256 extends AbstractCrypt * The class constructor. * * @param IL10N $localization The localization service. - * @param int $rounds The number of rounds. + * @param int $rounds The number of rounds. * This value must be between 1000 and 999999999. */ public function __construct(IL10N $localization, $rounds = 5000) @@ -54,7 +55,7 @@ class CryptSHA256 extends AbstractCrypt */ public function configuration() { - return [new CryptoParam("Rounds", 5000, 1000, 999999999)]; + return [new IntParam("Rounds", 5000, 1000, 999999999)]; } /** diff --git a/lib/Crypto/CryptSHA512.php b/lib/Crypto/CryptSHA512.php index 1cafee5..7048b77 100644 --- a/lib/Crypto/CryptSHA512.php +++ b/lib/Crypto/CryptSHA512.php @@ -21,6 +21,7 @@ namespace OCA\UserSQL\Crypto; +use OCA\UserSQL\Crypto\Param\IntParam; use OCP\IL10N; /** @@ -40,7 +41,7 @@ class CryptSHA512 extends AbstractCrypt * The class constructor. * * @param IL10N $localization The localization service. - * @param int $rounds The number of rounds. + * @param int $rounds The number of rounds. * This value must be between 1000 and 999999999. */ public function __construct(IL10N $localization, $rounds = 5000) @@ -54,7 +55,7 @@ class CryptSHA512 extends AbstractCrypt */ public function configuration() { - return [new CryptoParam("Rounds", 5000, 1000, 999999999)]; + return [new IntParam("Rounds", 5000, 1000, 999999999)]; } /** diff --git a/lib/Crypto/HashHmac.php b/lib/Crypto/HashHmac.php new file mode 100644 index 0000000..76d4040 --- /dev/null +++ b/lib/Crypto/HashHmac.php @@ -0,0 +1,79 @@ + + * @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 OCA\UserSQL\Crypto; + +use OCA\UserSQL\Crypto\Param\ChoiceParam; +use OCP\IL10N; + +/** + * HMAC hash implementation. + * + * @see hash_hmac() + * @author Marcin Łojewski + */ +class HashHmac extends AbstractAlgorithm +{ + const DEFAULT_ALGORITHM = "ripemd160"; + + /** + * @var string Hashing algorithm name. + */ + private $hashingAlgorithm; + + /** + * The class constructor. + * + * @param IL10N $localization The localization service. + * @param string $hashingAlgorithm Hashing algorithm name. + */ + public function __construct(IL10N $localization, $hashingAlgorithm = self::DEFAULT_ALGORITHM) + { + parent::__construct($localization); + $this->hashingAlgorithm = $hashingAlgorithm; + } + + /** + * @inheritdoc + */ + public function getPasswordHash($password, $salt = null) + { + return hash_hmac($this->hashingAlgorithm, $password, $salt); + } + + /** + * @inheritdoc + */ + public function configuration() + { + return [ + new ChoiceParam("Hashing algorithm", self::DEFAULT_ALGORITHM, hash_hmac_algos()) + ]; + } + + /** + * @inheritdoc + */ + protected function getAlgorithmName() + { + return "Hash HMAC"; + } +} diff --git a/lib/Crypto/Param/ChoiceParam.php b/lib/Crypto/Param/ChoiceParam.php new file mode 100644 index 0000000..d8ea216 --- /dev/null +++ b/lib/Crypto/Param/ChoiceParam.php @@ -0,0 +1,50 @@ + + * @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 OCA\UserSQL\Crypto\Param; + +/** + * A choice parameter of a hash algorithm. + * + * @author Marcin Łojewski + */ +class ChoiceParam extends CryptoParam +{ + const TYPE = "choice"; + + /** + * @var array Available choices. + */ + public $choices; + + /** + * Class constructor. + * + * @param $name string Parameter name. + * @param $value mixed Parameter default value. + * @param $choices array Available choices. + */ + public function __construct($name, $value = null, $choices = []) + { + parent::__construct(self::TYPE, $name, $value); + $this->choices = $choices; + } +} diff --git a/lib/Crypto/Param/CryptoParam.php b/lib/Crypto/Param/CryptoParam.php new file mode 100644 index 0000000..ea9c75e --- /dev/null +++ b/lib/Crypto/Param/CryptoParam.php @@ -0,0 +1,57 @@ + + * @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 OCA\UserSQL\Crypto\Param; + +/** + * A parameter of a hash algorithm. + * + * @author Marcin Łojewski + */ +class CryptoParam +{ + /** + * @var string Type name used in JS. + */ + public $type; + /** + * @var string Parameter name. + */ + public $name; + /** + * @var mixed Parameter default value. + */ + public $value; + + /** + * Class constructor. + * + * @param $type string Type name used in JS. + * @param $name string Parameter name. + * @param $value mixed Parameter default value. + */ + public function __construct($type, $name, $value = null) + { + $this->type = $type; + $this->name = $name; + $this->value = $value; + } +} diff --git a/lib/Crypto/CryptoParam.php b/lib/Crypto/Param/IntParam.php similarity index 80% rename from lib/Crypto/CryptoParam.php rename to lib/Crypto/Param/IntParam.php index 773f356..6b04a5a 100644 --- a/lib/Crypto/CryptoParam.php +++ b/lib/Crypto/Param/IntParam.php @@ -2,7 +2,7 @@ /** * Nextcloud - user_sql * - * @copyright 2018 Marcin Łojewski + * @copyright 2020 Marcin Łojewski * @author Marcin Łojewski * * This program is free software: you can redistribute it and/or modify @@ -19,23 +19,17 @@ * along with this program. If not, see . */ -namespace OCA\UserSQL\Crypto; +namespace OCA\UserSQL\Crypto\Param; /** - * A parameter of a hash algorithm. + * An integer parameter of a hash algorithm. * * @author Marcin Łojewski */ -class CryptoParam +class IntParam extends CryptoParam { - /** - * @var string Parameter name. - */ - public $name; - /** - * @var int Parameter default value. - */ - public $value; + const TYPE = "int"; + /** * @var int Minimal value for parameter. */ @@ -55,8 +49,7 @@ class CryptoParam */ public function __construct($name, $value = null, $min = null, $max = null) { - $this->name = $name; - $this->value = $value; + parent::__construct(self::TYPE, $name, $value); $this->min = $min; $this->max = $max; } diff --git a/lib/Crypto/Phpass.php b/lib/Crypto/Phpass.php index 7edd728..4b117a9 100644 --- a/lib/Crypto/Phpass.php +++ b/lib/Crypto/Phpass.php @@ -21,6 +21,7 @@ namespace OCA\UserSQL\Crypto; +use OCA\UserSQL\Crypto\Param\IntParam; use OCP\IL10N; /** @@ -160,7 +161,7 @@ class Phpass extends AbstractAlgorithm */ public function configuration() { - return [new CryptoParam("Iterations (log2)", 8, 4, 31)]; + return [new IntParam("Iterations (log2)", 8, 4, 31)]; } /** diff --git a/templates/admin.php b/templates/admin.php index e306adc..036a639 100644 --- a/templates/admin.php +++ b/templates/admin.php @@ -19,6 +19,7 @@ * along with this program. If not, see . */ +use OCA\UserSQL\Crypto\IPasswordAlgorithm; use OCP\IL10N; script("user_sql", "settings"); @@ -125,14 +126,13 @@ function print_select_options( $class = "OCA\\UserSQL\\Crypto\\" . basename(substr($filename, 0, -4)); try { $passwordAlgorithm = new $class($l); - if ($passwordAlgorithm instanceof - \OCA\UserSQL\Crypto\IPasswordAlgorithm - ) { + if ($passwordAlgorithm instanceof IPasswordAlgorithm) { $hashes[$class] = $passwordAlgorithm->getVisibleName(); } } catch (Throwable $e) { } } + asort($hashes); print_select_options($l, "opt-crypto_class", "Hash algorithm", $hashes, $_["opt.crypto_class"]); ?>