Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F17719586
D10387.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
D10387.id.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2112,6 +2112,7 @@
'PhabricatorSavedQuery' => 'applications/search/storage/PhabricatorSavedQuery.php',
'PhabricatorSavedQueryQuery' => 'applications/search/query/PhabricatorSavedQueryQuery.php',
'PhabricatorScopedEnv' => 'infrastructure/env/PhabricatorScopedEnv.php',
+ 'PhabricatorScryptPasswordHasher' => 'infrastructure/util/password/PhabricatorScryptPasswordHasher.php',
'PhabricatorSearchAbstractDocument' => 'applications/search/index/PhabricatorSearchAbstractDocument.php',
'PhabricatorSearchApplication' => 'applications/search/application/PhabricatorSearchApplication.php',
'PhabricatorSearchApplicationSearchEngine' => 'applications/search/query/PhabricatorSearchApplicationSearchEngine.php',
@@ -5012,6 +5013,7 @@
'PhabricatorPolicyInterface',
),
'PhabricatorSavedQueryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorScryptPasswordHasher' => 'PhabricatorPasswordHasher',
'PhabricatorSearchApplication' => 'PhabricatorApplication',
'PhabricatorSearchApplicationSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorSearchAttachController' => 'PhabricatorSearchBaseController',
diff --git a/src/infrastructure/util/password/PhabricatorScryptPasswordHasher.php b/src/infrastructure/util/password/PhabricatorScryptPasswordHasher.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/util/password/PhabricatorScryptPasswordHasher.php
@@ -0,0 +1,129 @@
+<?php
+
+final class PhabricatorScryptPasswordHasher
+ extends PhabricatorPasswordHasher {
+
+ public function getHumanReadableName() {
+ return pht('scrypt');
+ }
+
+ public function getHashName() {
+ return 'scrypt';
+ }
+
+ public function getHashLength() {
+ // 'logN', 'r', and 'p' are each 3 bytes.
+ // There are four '|' separators, one byte each.
+ // The salt is 16 bytes.
+ // The hash is 40 bytes, but converted to a hex string, so 80 bytes total.
+ // So: 3 + 3 + 3 + 4 + 16 + (40*2) = 109
+ return 109;
+ }
+
+ public function canHashPasswords() {
+ return extension_loaded('scrypt') && function_exists('scrypt');
+ }
+
+ public function getInstallInstructions() {
+ return pht('Install the `scrypt` extension via PECL/PEAR.');
+ }
+
+ public function getStrength() {
+ return 4.0;
+ }
+
+ public function getHumanReadableStrength() {
+ return pht('Great');
+ }
+
+ protected function getPasswordHash(PhutilOpaqueEnvelope $envelope) {
+ list($log_n, $r, $p) = $this->getScryptParams();
+ $salt = Filesystem::readRandomCharacters(16);
+
+ return new PhutilOpaqueEnvelope(
+ $this->createHash($envelope->openEnvelope(), $salt, $log_n, $r, $p));
+ }
+
+ protected function verifyPassword(
+ PhutilOpaqueEnvelope $password,
+ PhutilOpaqueEnvelope $hash) {
+ return $this->verifyPass($password->openEnvelope(), $hash->openEnvelope());
+ }
+
+ protected function canUpgradeInternalHash(PhutilOpaqueEnvelope $hash) {
+ $info = $this->getParams($hash->openEnvelope());
+
+ // NOTE: If the costs don't match -- even if the new cost is lower than
+ // the old cost -- count this as an upgrade. This allows costs to be
+ // adjusted down and hashing to be migrated toward the new cost if costs
+ // are ever configured too high for some reason.
+
+ if ($info !== $this->getScryptParams()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function getScryptParams() {
+ // Recommended interactive scrypt parameters (log2(N), r, p)
+ // - NOTE: /n/ is specified as log2(N)
+ //
+ // Running time is proportional to `r`, `n`, and `p`.
+ //
+ // Memory usage is approximately `128*r*n` bytes. Default settings are
+ // `128*(2^14)*8 = 16MB`.
+ //
+ // `p` can be independently tuned to influence running time since it has
+ // low influence on memory use.
+ return array(14, 8, 1);
+ }
+
+ /**
+ * Verify a password against a serialized hash
+ */
+ private function verifyPass($pass, $hash) {
+ list($log_n, $r, $p, $salt, $raw_hash) = $this->separateHash($hash);
+
+ $pass_hash = $this->createHash($pass, $salt, $log_n, $r, $p);
+ return $pass_hash === $hash; /* TODO FIXME: constant time comparison? */
+ }
+
+ /**
+ * Create a fully 'serialized' hash with included parameters.
+ *
+ * @phutil-external-symbol function scrypt
+ */
+ private function createHash($raw_input, $salt, $log_n, $r, $p) {
+ $hash = scrypt(
+ $raw_input, $salt,
+ pow(2, intval($log_n)), intval($r), intval($p),
+ 40);
+
+ // Format numbers to three decimal places for accurate hash
+ // lengths, since the 40 byte output and 16 byte salt are
+ // statically known. (Someone could set `p = 10` for example,
+ // which would break things with naive formatting.)
+ $log_n = sprintf('%03d', $log_n);
+ $r = sprintf('%03d', $r);
+ $p = sprintf('%03d', $p);
+ return implode('|', array($log_n, $r, $p, $salt, $hash));
+ }
+
+ /**
+ * Get the parameters used for a hashed password.
+ */
+ private function getParams($hash) {
+ list($log_n, $r, $p, $salt, $raw_hash) = $this->separateHash($hash);
+ return array($log_n, $r, $p);
+ }
+
+ /**
+ * Split a hashed password into its internal components.
+ */
+ private function separateHash($hash) {
+ list($log_n, $r, $p, $salt, $raw_hash) = explode('|', $hash);
+ return array(intval($log_n), intval($r), intval($p), $salt, $raw_hash);
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jul 19, 4:09 AM (2 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
8492879
Default Alt Text
D10387.id.diff (5 KB)
Attached To
Mode
D10387: Add support for scrypt-based password hashing.
Attached
Detach File
Event Timeline
Log In to Comment