mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Add SCryptModified logic
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
# This configuration file was automatically generated by Gitpod.
|
||||
# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file)
|
||||
# and commit this file to your remote git repository to share the goodness with others.
|
||||
|
||||
tasks:
|
||||
- init: npm install && npm run build
|
||||
vscode:
|
||||
extensions:
|
||||
- dbaeumer.vscode-eslint
|
||||
|
||||
Generated
+246
-245
File diff suppressed because it is too large
Load Diff
Vendored
+79
-68
File diff suppressed because one or more lines are too long
Vendored
+79
-68
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ use Appwrite\Auth\Hash\BCrypt;
|
||||
use Appwrite\Auth\Hash\MD5;
|
||||
use Appwrite\Auth\Hash\PHPass;
|
||||
use Appwrite\Auth\Hash\SCrypt;
|
||||
use Appwrite\Auth\Hash\SCryptModified;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
@@ -148,6 +149,10 @@ class Auth
|
||||
$hasher = new BCrypt($options);
|
||||
$hash = $hasher->hash($string);
|
||||
return $hash;
|
||||
case 'scrypt_mod':
|
||||
$hasher = new SCryptModified($options);
|
||||
$hash = $hasher->hash($string);
|
||||
return $hash;
|
||||
case 'scrypt':
|
||||
$hasher = new SCrypt($options);
|
||||
$hash = $hasher->hash($string);
|
||||
@@ -157,6 +162,7 @@ class Auth
|
||||
$hash = $hasher->hash($string);
|
||||
return $hash;
|
||||
case 'phpass':
|
||||
// TODO: Rework to use abstract class
|
||||
$hahser = new PHPass(8, FALSE);
|
||||
$hash = $hahser->hash($string);
|
||||
return $hash;
|
||||
@@ -177,13 +183,16 @@ class Auth
|
||||
*/
|
||||
public static function passwordVerify(string $plain, string $hash, string $algo, mixed $options = [])
|
||||
{
|
||||
|
||||
// TODO: Abstract, somehow.
|
||||
switch ($algo) {
|
||||
case 'bcrypt':
|
||||
$hasher = new BCrypt($options);
|
||||
$verify = $hasher->verify($plain, $hash);
|
||||
return $verify;
|
||||
case 'scrypt_mod':
|
||||
$hasher = new SCryptModified($options);
|
||||
$verify = $hasher->verify($plain, $hash);
|
||||
return $verify;
|
||||
case 'scrypt':
|
||||
$hasher = new SCrypt($options ?? [ 'cost_cpu' => 8, 'cost_memory' => 14, 'cost_parallel' => 1, 'length' => 64 ]);
|
||||
$verify = $hasher->verify($plain, $hash);
|
||||
@@ -193,7 +202,7 @@ class Auth
|
||||
$verify = $hasher->verify($plain, $hash);
|
||||
return $verify;
|
||||
case 'phpass':
|
||||
// TODO: Support options
|
||||
// TODO: Rework to use abstract class
|
||||
$hahser = new PHPass(8, FALSE);
|
||||
$verify = $hahser->verify($plain, $hash);
|
||||
return $verify;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Hash;
|
||||
|
||||
use Appwrite\Auth\Hash;
|
||||
|
||||
/*
|
||||
* This is SCrypt hash with some additional steps added by Google.
|
||||
*
|
||||
* string salt
|
||||
* string salt_separator
|
||||
* strin signer_key
|
||||
*
|
||||
* Refference: https://github.com/DomBlack/php-scrypt/blob/master/scrypt.php#L112-L116
|
||||
*/
|
||||
class SCryptModified extends Hash
|
||||
{
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
public function hash(string $password): string {
|
||||
$options = $this->getOptions();
|
||||
|
||||
$derivedKeyBytes = $this->generateDerivedKey($password);
|
||||
$signerKeyBytes = \base64_decode($options['signer_key']);
|
||||
|
||||
$hashedPassword = $this->hashKeys($signerKeyBytes, $derivedKeyBytes);
|
||||
|
||||
return \base64_encode($hashedPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
public function verify(string $password, string $hash): bool {
|
||||
|
||||
return $hash === $this->hash($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return mixed options named array
|
||||
*/
|
||||
public function getDefaultOptions(): mixed {
|
||||
return [ ];
|
||||
}
|
||||
|
||||
private function generateDerivedKey(string $password) {
|
||||
$options = $this->getOptions();
|
||||
|
||||
$saltBytes = \base64_decode($options['salt']);
|
||||
$saltSeparatorBytes = \base64_decode($options['salt_separator']);
|
||||
|
||||
$derivedKey = \scrypt(\utf8_encode($password), $saltBytes . $saltSeparatorBytes, 16384, 8, 1, 64, true);
|
||||
|
||||
return $derivedKey;
|
||||
}
|
||||
|
||||
private function hashKeys($signerKeyBytes, $derivedKeyBytes): string {
|
||||
$key = \substr($derivedKeyBytes, 0, 32);
|
||||
|
||||
$iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
|
||||
$hash = \openssl_encrypt($signerKeyBytes, 'aes-256-ctr', $key, OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,9 @@ class AuthTest extends TestCase
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'phpass'));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'phpass'));
|
||||
|
||||
// SCryptModified
|
||||
// TODO: Add tests
|
||||
|
||||
/*
|
||||
Provider-specific tests, ensuring functionality of specific use-cases
|
||||
*/
|
||||
@@ -122,11 +125,14 @@ class AuthTest extends TestCase
|
||||
// Provider #2 (Google)
|
||||
$plain = 'users-password';
|
||||
$hash = 'EPKgfALpS9Tvgr/y1ki7ubY4AEGJeWL3teakrnmOacN4XGiyD00lkzEHgqCQ71wGxoi/zb7Y9a4orOtvMV3/Jw==';
|
||||
$options = [ 'salt' => '56dFqW+kswqktw==', 'cost_cpu' => 8, 'cost_memory' => 14, 'cost_parallel' => 1, 'length' => 64 ];
|
||||
$generatedHash = Auth::passwordHash($plain, 'scrypt', $options);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'scrypt', $options));
|
||||
// TODO: This test is failing.
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'scrypt', $options));
|
||||
$salt = '56dFqW+kswqktw==';
|
||||
$saltSeparator = 'Bw==';
|
||||
$signerKey = 'XyEKE9RcTDeLEsL/RjwPDBv/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ==';
|
||||
|
||||
$options = [ 'salt' => $salt, 'salt_separator' => $saltSeparator, 'signer_key' => $signerKey ];
|
||||
$generatedHash = Auth::passwordHash($plain, 'scrypt_mod', $options);
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'scrypt_mod', $options));
|
||||
$this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'scrypt_mod', $options));
|
||||
}
|
||||
|
||||
public function testPasswordGenerator()
|
||||
|
||||
Reference in New Issue
Block a user