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({
+ id: "opt-crypto_param_" + index,
+ name: "opt-crypto_param_" + index,
+ });
+ data.data[index]["choices"].forEach(
+ function (item) {
+ if (data.data[index]["value"] === item) {
+ input.append($("").attr({
+ value: item,
+ selected: "selected"
+ }).text(item));
+ } else {
+ input.append($("").attr({value: item}).text(item));
+ }
+ }
+ );
+ break;
+ case "int":
+ 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"]); ?>
diff --git a/tests/Crypto/HashHmacTest.php b/tests/Crypto/HashHmacTest.php
new file mode 100644
index 0000000..1696933
--- /dev/null
+++ b/tests/Crypto/HashHmacTest.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 Tests\UserSQL\Crypto;
+
+use OCA\UserSQL\Crypto\HashHmac;
+use OCA\UserSQL\Crypto\IPasswordAlgorithm;
+use OCP\IL10N;
+use Test\TestCase;
+
+/**
+ * Unit tests for class HashHmac.
+ *
+ * @author Marcin Łojewski
+ */
+class HashHmacTest extends TestCase
+{
+ /**
+ * @var IPasswordAlgorithm
+ */
+ private $crypto;
+
+ public function testCheckPassword()
+ {
+ $this->assertTrue($this->crypto->checkPassword("password", "ba4f8624f0a4d1f2a3991f4d88cd9afb604dac20"));
+ }
+
+ public function testPasswordHash()
+ {
+ $hash = $this->crypto->getPasswordHash("password");
+ $this->assertTrue($this->crypto->checkPassword("password", $hash));
+ }
+
+ protected function setUp(): void
+ {
+ parent::setUp();
+ $this->crypto = new HashHmac($this->createMock(IL10N::class));
+ }
+}
diff --git a/tests/Crypto/SHA256Test.php b/tests/Crypto/SHA256Test.php
index 865083b..5f26324 100644
--- a/tests/Crypto/SHA256Test.php
+++ b/tests/Crypto/SHA256Test.php
@@ -31,7 +31,7 @@ use Test\TestCase;
*
* @author Marcin Łojewski
*/
-class SHA512Test extends TestCase
+class SHA256Test extends TestCase
{
/**
* @var IPasswordAlgorithm