/**
* Hashing
+ */
+
+/**
+ * By default Nextcloud will use the Argon2 password hashing if available.
+ * However if for whatever reason you want to stick with the PASSWORD_DEFAULT
+ * of your php version. Then set the setting to true.
+ */
+'hashing_default_password' => false,
+
+/**
*
* Nextcloud uses the Argon2 algorithm (with PHP >= 7.2) to create hashes by its
* own and exposes its configuration options as following. More information can
* @return string Hash of the message with appended version parameter
*/
public function hash(string $message): string {
- if (\defined('PASSWORD_ARGON2I')) {
+ $alg = $this->getPrefferedAlgorithm();
+
+ if (\defined('PASSWORD_ARGON2I') && $alg === PASSWORD_ARGON2I) {
return 2 . '|' . password_hash($message, PASSWORD_ARGON2I, $this->options);
- } else {
- return 1 . '|' . password_hash($message, PASSWORD_BCRYPT, $this->options);
}
+
+ return 1 . '|' . password_hash($message, PASSWORD_BCRYPT, $this->options);
}
/**
*/
protected function verifyHashV1(string $message, string $hash, &$newHash = null): bool {
if(password_verify($message, $hash)) {
- $algo = PASSWORD_BCRYPT;
- if (\defined('PASSWORD_ARGON2I')) {
- $algo = PASSWORD_ARGON2I;
- }
-
- if(password_needs_rehash($hash, $algo, $this->options)) {
+ if ($this->needsRehash($hash)) {
$newHash = $this->hash($message);
}
return true;
*/
protected function verifyHashV2(string $message, string $hash, &$newHash = null) : bool {
if(password_verify($message, $hash)) {
- if(password_needs_rehash($hash, PASSWORD_ARGON2I, $this->options)) {
+ if($this->needsRehash($hash)) {
$newHash = $this->hash($message);
}
return true;
return $this->legacyHashVerify($message, $hash, $newHash);
}
-
return false;
}
+ private function needsRehash(string $hash): bool {
+ $algorithm = $this->getPrefferedAlgorithm();
+
+ return password_needs_rehash($hash, $algorithm, $this->options);
+ }
+
+ private function getPrefferedAlgorithm() {
+ $default = PASSWORD_BCRYPT;
+ if (\defined('PASSWORD_ARGON2I')) {
+ $default = PASSWORD_ARGON2I;
+ }
+
+ // Check if we should use PASSWORD_DEFAULT
+ if ($this->config->getSystemValue('hashing_default_password', false) === true) {
+ $default = PASSWORD_DEFAULT;
+ }
+
+ return $default;
+ }
+
}
$this->config
->expects($this->any())
->method('getSystemValue')
- ->with('passwordsalt', null)
- ->will($this->returnValue('6Wow67q1wZQZpUUeI6G2LsWUu4XKx'));
+ ->willReturnCallback(function ($key, $default) {
+ if($key === 'passwordsalt') {
+ return '6Wow67q1wZQZpUUeI6G2LsWUu4XKx';
+ }
+ return $default;
+ });
$result = $this->hasher->verify($password, $hash);
$this->assertSame($expected, $result);
$this->assertFalse(password_needs_rehash($relativePath['hash'], PASSWORD_ARGON2I, []));
}
+
+ public function testUsePasswordDefaultArgon2iVerify() {
+ if (!\defined('PASSWORD_ARGON2I')) {
+ $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
+ }
+
+ $this->config->method('getSystemValue')
+ ->with('hashing_default_password')
+ ->willReturn(true);
+
+ $message = 'mysecret';
+
+ $argon2i = 2 . '|' . password_hash($message, PASSWORD_ARGON2I, []);
+
+ $newHash = null;
+ $this->assertTrue($this->hasher->verify($message, $argon2i, $newHash));
+ $this->assertNotNull($newHash);
+ }
+
+ public function testDoNotUserPasswordDefaultArgon2iVerify() {
+ if (!\defined('PASSWORD_ARGON2I')) {
+ $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
+ }
+
+ $this->config->method('getSystemValue')
+ ->with('hashing_default_password')
+ ->willReturn(false);
+
+ $message = 'mysecret';
+
+ $argon2i = 2 . '|' . password_hash($message, PASSWORD_ARGON2I, []);
+
+ $newHash = null;
+ $this->assertTrue($this->hasher->verify($message, $argon2i, $newHash));
+ $this->assertNull($newHash);
+ }
+
+ public function testHashUsePasswordDefault() {
+ if (!\defined('PASSWORD_ARGON2I')) {
+ $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
+ }
+
+ $this->config->method('getSystemValue')
+ ->with('hashing_default_password')
+ ->willReturn(true);
+
+ $message = 'mysecret';
+
+ $hash = $this->hasher->hash($message);
+ $relativePath = self::invokePrivate($this->hasher, 'splitHash', [$hash]);
+
+ $this->assertSame(1, $relativePath['version']);
+
+ $info = password_get_info($relativePath['hash']);
+ $this->assertEquals(PASSWORD_BCRYPT, $info['algo']);
+
+ }
}