Page MenuHomePhabricator

D10387.id.diff
No OneTemporary

D10387.id.diff

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

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)

Event Timeline