Hash HMAC algo

This commit is contained in:
Marcin Łojewski
2020-04-13 14:10:27 +02:00
parent 7e9af00145
commit d7735280a0
18 changed files with 330 additions and 48 deletions

View File

@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Allow email login option - Allow email login option
- UID user table column - UID user table column
- GID user table column - GID user table column
- HMAC hash implementation
## [4.4.1] - 2020-02-02 ## [4.4.1] - 2020-02-02
### Fixed ### Fixed

View File

@@ -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 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 Blowfish (Crypt) | See [password_hash](http://php.net/manual/en/function.password-hash.php). | $2y$10$5rsN1fmoSkaRy9bqhozAXOr0mn0QiVIfd2L04Bbk1Go9MjdvotwBq
Extended DES (Crypt) | | cDRpdxPmHpzS. 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/ MD5 (Crypt) | | $1$RzaFbNcU$u9adfTY/Q6za6nu0Ogrl1/
SHA256 (Crypt) | | $5$rounds=5000$VIYD0iHkg7uY9SRc$v2XLS/9dvfFN84mzGvW9wxnVt9Xd/urXaaTkpW8EwD1 SHA256 (Crypt) | | $5$rounds=5000$VIYD0iHkg7uY9SRc$v2XLS/9dvfFN84mzGvW9wxnVt9Xd/urXaaTkpW8EwD1
SHA512 (Crypt) | | $6$rounds=5000$yH.Q0OL4qbCOUJ3q$Xry5EVFva3wKnfo8/ktrugmBd8tcl34NK6rXInv1HhmdSUNLEm0La9JnA57rqwQ.9/Bz513MD4tvmmISLUIHs/ SHA512 (Crypt) | | $6$rounds=5000$yH.Q0OL4qbCOUJ3q$Xry5EVFva3wKnfo8/ktrugmBd8tcl34NK6rXInv1HhmdSUNLEm0La9JnA57rqwQ.9/Bz513MD4tvmmISLUIHs/

View File

@@ -76,7 +76,29 @@ user_sql.adminSettingsUI = function () {
var param = $("<div></div>"); var param = $("<div></div>");
var label = $("<label></label>").attr({for: "opt-crypto_param_" + index}); var label = $("<label></label>").attr({for: "opt-crypto_param_" + index});
var title = $("<span></span>").text(data.data[index]["name"]); var title = $("<span></span>").text(data.data[index]["name"]);
var input = $("<input/>").attr({
var input = null;
switch (data.data[index]["type"]) {
case "choice":
input = $("<select/>").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($("<option/>").attr({
value: item,
selected: "selected"
}).text(item));
} else {
input.append($("<option/>").attr({value: item}).text(item));
}
}
);
break;
case "int":
input = $("<input/>").attr({
type: "number", type: "number",
id: "opt-crypto_param_" + index, id: "opt-crypto_param_" + index,
name: "opt-crypto_param_" + index, name: "opt-crypto_param_" + index,
@@ -85,6 +107,10 @@ user_sql.adminSettingsUI = function () {
max: data.data[index]["max"], max: data.data[index]["max"],
value: data.data[index]["value"] value: data.data[index]["value"]
}); });
break;
default:
break;
}
label.append(title); label.append(title);
param.append(label); param.append(label);

View File

@@ -31,6 +31,8 @@ use OCA\UserSQL\Constant\App;
use OCA\UserSQL\Constant\DB; use OCA\UserSQL\Constant\DB;
use OCA\UserSQL\Constant\Opt; use OCA\UserSQL\Constant\Opt;
use OCA\UserSQL\Crypto\IPasswordAlgorithm; 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\Platform\PlatformFactory;
use OCA\UserSQL\Properties; use OCA\UserSQL\Properties;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
@@ -77,8 +79,7 @@ class SettingsController extends Controller
public function __construct( public function __construct(
$appName, IRequest $request, ILogger $logger, IL10N $localization, $appName, IRequest $request, ILogger $logger, IL10N $localization,
Properties $properties, Cache $cache Properties $properties, Cache $cache
) ) {
{
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->appName = $appName; $this->appName = $appName;
$this->logger = $logger; $this->logger = $logger;
@@ -267,13 +268,23 @@ class SettingsController extends Controller
$reqParam = $this->request->getParam( $reqParam = $this->request->getParam(
"opt-crypto_param_" . $i, null "opt-crypto_param_" . $i, null
); );
$cryptoParam = $configuration[$i]; if (is_null($reqParam)) {
if (is_null($reqParam) || $reqParam < $cryptoParam->min
|| $reqParam > $cryptoParam->max
) {
return false; 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; return true;

View File

@@ -21,6 +21,7 @@
namespace OCA\UserSQL\Crypto; namespace OCA\UserSQL\Crypto;
use OCA\UserSQL\Crypto\Param\IntParam;
use OCP\IL10N; use OCP\IL10N;
/** /**
@@ -106,14 +107,14 @@ class CryptArgon2 extends AbstractAlgorithm
public function configuration() public function configuration()
{ {
return [ return [
new CryptoParam( new IntParam(
"Memory cost (KiB)", PASSWORD_ARGON2_DEFAULT_MEMORY_COST, 1, "Memory cost (KiB)", PASSWORD_ARGON2_DEFAULT_MEMORY_COST, 1,
1048576 1048576
), ),
new CryptoParam( new IntParam(
"Time cost", PASSWORD_ARGON2_DEFAULT_TIME_COST, 1, 1024 "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)
]; ];
} }

View File

@@ -21,6 +21,7 @@
namespace OCA\UserSQL\Crypto; namespace OCA\UserSQL\Crypto;
use OCA\UserSQL\Crypto\Param\IntParam;
use OCP\IL10N; use OCP\IL10N;
/** /**
@@ -106,14 +107,14 @@ class CryptArgon2id extends AbstractAlgorithm
public function configuration() public function configuration()
{ {
return [ return [
new CryptoParam( new IntParam(
"Memory cost (KiB)", PASSWORD_ARGON2_DEFAULT_MEMORY_COST, 1, "Memory cost (KiB)", PASSWORD_ARGON2_DEFAULT_MEMORY_COST, 1,
1048576 1048576
), ),
new CryptoParam( new IntParam(
"Time cost", PASSWORD_ARGON2_DEFAULT_TIME_COST, 1, 1024 "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)
]; ];
} }

View File

@@ -21,6 +21,7 @@
namespace OCA\UserSQL\Crypto; namespace OCA\UserSQL\Crypto;
use OCA\UserSQL\Crypto\Param\IntParam;
use OCP\IL10N; use OCP\IL10N;
/** /**
@@ -72,7 +73,7 @@ class CryptBlowfish extends AbstractAlgorithm
*/ */
public function configuration() public function configuration()
{ {
return [new CryptoParam("Cost", 10, 4, 31)]; return [new IntParam("Cost", 10, 4, 31)];
} }
/** /**

View File

@@ -21,6 +21,7 @@
namespace OCA\UserSQL\Crypto; namespace OCA\UserSQL\Crypto;
use OCA\UserSQL\Crypto\Param\IntParam;
use OCP\IL10N; use OCP\IL10N;
/** /**
@@ -53,7 +54,7 @@ class CryptExtendedDES extends AbstractCrypt
*/ */
public function configuration() public function configuration()
{ {
return [new CryptoParam("Iterations", 1000, 0, 16777215)]; return [new IntParam("Iterations", 1000, 0, 16777215)];
} }
/** /**

View File

@@ -21,6 +21,7 @@
namespace OCA\UserSQL\Crypto; namespace OCA\UserSQL\Crypto;
use OCA\UserSQL\Crypto\Param\IntParam;
use OCP\IL10N; use OCP\IL10N;
/** /**
@@ -54,7 +55,7 @@ class CryptSHA256 extends AbstractCrypt
*/ */
public function configuration() public function configuration()
{ {
return [new CryptoParam("Rounds", 5000, 1000, 999999999)]; return [new IntParam("Rounds", 5000, 1000, 999999999)];
} }
/** /**

View File

@@ -21,6 +21,7 @@
namespace OCA\UserSQL\Crypto; namespace OCA\UserSQL\Crypto;
use OCA\UserSQL\Crypto\Param\IntParam;
use OCP\IL10N; use OCP\IL10N;
/** /**
@@ -54,7 +55,7 @@ class CryptSHA512 extends AbstractCrypt
*/ */
public function configuration() public function configuration()
{ {
return [new CryptoParam("Rounds", 5000, 1000, 999999999)]; return [new IntParam("Rounds", 5000, 1000, 999999999)];
} }
/** /**

79
lib/Crypto/HashHmac.php Normal file
View File

@@ -0,0 +1,79 @@
<?php
/**
* Nextcloud - user_sql
*
* @copyright 2020 Marcin Łojewski <dev@mlojewski.me>
* @author Marcin Łojewski <dev@mlojewski.me>
*
* 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 <https://www.gnu.org/licenses/>.
*/
namespace OCA\UserSQL\Crypto;
use OCA\UserSQL\Crypto\Param\ChoiceParam;
use OCP\IL10N;
/**
* HMAC hash implementation.
*
* @see hash_hmac()
* @author Marcin Łojewski <dev@mlojewski.me>
*/
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";
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Nextcloud - user_sql
*
* @copyright 2020 Marcin Łojewski <dev@mlojewski.me>
* @author Marcin Łojewski <dev@mlojewski.me>
*
* 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 <https://www.gnu.org/licenses/>.
*/
namespace OCA\UserSQL\Crypto\Param;
/**
* A choice parameter of a hash algorithm.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
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;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* Nextcloud - user_sql
*
* @copyright 2020 Marcin Łojewski <dev@mlojewski.me>
* @author Marcin Łojewski <dev@mlojewski.me>
*
* 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 <https://www.gnu.org/licenses/>.
*/
namespace OCA\UserSQL\Crypto\Param;
/**
* A parameter of a hash algorithm.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
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;
}
}

View File

@@ -2,7 +2,7 @@
/** /**
* Nextcloud - user_sql * Nextcloud - user_sql
* *
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me> * @copyright 2020 Marcin Łojewski <dev@mlojewski.me>
* @author Marcin Łojewski <dev@mlojewski.me> * @author Marcin Łojewski <dev@mlojewski.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@@ -19,23 +19,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
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 <dev@mlojewski.me> * @author Marcin Łojewski <dev@mlojewski.me>
*/ */
class CryptoParam class IntParam extends CryptoParam
{ {
/** const TYPE = "int";
* @var string Parameter name.
*/
public $name;
/**
* @var int Parameter default value.
*/
public $value;
/** /**
* @var int Minimal value for parameter. * @var int Minimal value for parameter.
*/ */
@@ -55,8 +49,7 @@ class CryptoParam
*/ */
public function __construct($name, $value = null, $min = null, $max = null) public function __construct($name, $value = null, $min = null, $max = null)
{ {
$this->name = $name; parent::__construct(self::TYPE, $name, $value);
$this->value = $value;
$this->min = $min; $this->min = $min;
$this->max = $max; $this->max = $max;
} }

View File

@@ -21,6 +21,7 @@
namespace OCA\UserSQL\Crypto; namespace OCA\UserSQL\Crypto;
use OCA\UserSQL\Crypto\Param\IntParam;
use OCP\IL10N; use OCP\IL10N;
/** /**
@@ -160,7 +161,7 @@ class Phpass extends AbstractAlgorithm
*/ */
public function configuration() public function configuration()
{ {
return [new CryptoParam("Iterations (log2)", 8, 4, 31)]; return [new IntParam("Iterations (log2)", 8, 4, 31)];
} }
/** /**

View File

@@ -19,6 +19,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use OCA\UserSQL\Crypto\IPasswordAlgorithm;
use OCP\IL10N; use OCP\IL10N;
script("user_sql", "settings"); script("user_sql", "settings");
@@ -125,14 +126,13 @@ function print_select_options(
$class = "OCA\\UserSQL\\Crypto\\" . basename(substr($filename, 0, -4)); $class = "OCA\\UserSQL\\Crypto\\" . basename(substr($filename, 0, -4));
try { try {
$passwordAlgorithm = new $class($l); $passwordAlgorithm = new $class($l);
if ($passwordAlgorithm instanceof if ($passwordAlgorithm instanceof IPasswordAlgorithm) {
\OCA\UserSQL\Crypto\IPasswordAlgorithm
) {
$hashes[$class] = $passwordAlgorithm->getVisibleName(); $hashes[$class] = $passwordAlgorithm->getVisibleName();
} }
} catch (Throwable $e) { } catch (Throwable $e) {
} }
} }
asort($hashes);
print_select_options($l, "opt-crypto_class", "Hash algorithm", $hashes, $_["opt.crypto_class"]); ?> print_select_options($l, "opt-crypto_class", "Hash algorithm", $hashes, $_["opt.crypto_class"]); ?>
<div id="opt-crypto_params_loading" style="display: none"> <div id="opt-crypto_params_loading" style="display: none">

View File

@@ -0,0 +1,57 @@
<?php
/**
* Nextcloud - user_sql
*
* @copyright 2020 Marcin Łojewski <dev@mlojewski.me>
* @author Marcin Łojewski <dev@mlojewski.me>
*
* 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 <https://www.gnu.org/licenses/>.
*/
namespace Tests\UserSQL\Crypto;
use OCA\UserSQL\Crypto\HashHmac;
use OCA\UserSQL\Crypto\IPasswordAlgorithm;
use OCP\IL10N;
use Test\TestCase;
/**
* Unit tests for class <code>HashHmac</code>.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
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));
}
}

View File

@@ -31,7 +31,7 @@ use Test\TestCase;
* *
* @author Marcin Łojewski <dev@mlojewski.me> * @author Marcin Łojewski <dev@mlojewski.me>
*/ */
class SHA512Test extends TestCase class SHA256Test extends TestCase
{ {
/** /**
* @var IPasswordAlgorithm * @var IPasswordAlgorithm