From 3a0dd248d05e2b15a2c899fc72cb3f122c216917 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 15 Jun 2013 10:00:13 +0200 Subject: [PATCH] [feature/passwords] Add basic implementation of passwords manager The passwords manager is capable of checking and hashing passwords. It will support different hashing algorithms. PHPBB3-11610 --- phpBB/config/services.yml | 7 ++ phpBB/includes/crypto/manager.php | 200 ++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 phpBB/includes/crypto/manager.php diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml index 8abc413a5a..32dd2759b3 100644 --- a/phpBB/config/services.yml +++ b/phpBB/config/services.yml @@ -121,6 +121,13 @@ services: - @config - @dbal.conn + crypto.manager: + class: phpbb_crypto_manager + arguments: + - @config + - @service_container + - @crypto.driver_collection + dispatcher: class: phpbb_event_dispatcher arguments: diff --git a/phpBB/includes/crypto/manager.php b/phpBB/includes/crypto/manager.php new file mode 100644 index 0000000000..e54baba9b4 --- /dev/null +++ b/phpBB/includes/crypto/manager.php @@ -0,0 +1,200 @@ +config = $config; + $this->container = $container; + $this->type = 'phpbb_crypto_driver_bcrypt'; // might want to make this flexible + + $this->fill_type_map($hashing_algorithms); + $this->load_crypto_helper(); + } + + /** + * Fill algorithm type map + * + * @param phpbb_di_service_collection $hashing_algorithms + */ + protected function fill_type_map($hashing_algorithms) + { + if ($this->type_map !== false) + { + return; + } + + foreach ($hashing_algorithms as $algorithm) + { + if (!isset($this->type_map[$algorithm->get_prefix()])) + { + $this->type_map[$algorithm->get_prefix()] = $algorithm; + } + } + } + + /** + * Load crypto helper class + */ + protected function load_crypto_helper() + { + if ($this->helper === NULL) + { + $this->helper = new phpbb_crypto_helper($this); + } + } + + /** + * Get the hash type from the supplied hash + * + * @param string $hash Password hash that should be checked + * + * @return object The hash type object + * + * @throws RunTimeException If hash type is not supported + */ + public function get_hashing_algorithm($hash) + { + // preg_match() will also show hashing algos like $2a\H$, which + // is a combination of bcrypt and phpass + if (!preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $hash, $match)) + { + // Legacy support needed + throw new RunTimeException('NO_LEGACY_SUPPORT'); + } + + // Be on the lookout for multiple hashing algorithms + // 2 is correct: H\2a > 2, H\P > 2 + if (strlen($match[1]) > 2) + { + $hash_types = explode('\\', $match[1]); + $return_ary = array(); + foreach ($hash_types as $type) + { + if (isset($this->type_map["\${$type}\$"])) + { + $return_ary[$type] = $this->type_map["\${$type}\$"]; + } + else + { + throw new \RunTimeException('HASH_TYPE_NOT_SUPPORTED'); + } + } + return $return_ary; + } + if (isset($this->type_map[$match[0]])) + { + return $this->type_map[$match[0]]; + } + else + { + throw new RunTimeException('UNKNOWN_HASH_TYPE'); + } + } + + /** + * Hash supplied password + * + * @param string $password Password that should be hashed + * @param string $type Hash type. Will default to standard hash type if + * none is supplied + * @return string Password hash of supplied password + * + * @throws RunTimeException If hash type is not supported + */ + public function hash_password($password, $type = '') + { + if ($type === '') + { + return $this->container->get($this->type)->hash($password); + } + else + { + return $this->container->get($type)->hash($password); + } + } + + public function check_hash($password, $hash) + { + if (!$this->type_map) + { + // This obviously shouldn't happen + return false; + } + + // First find out what kind of hash we're dealing with + $stored_hash_type = $this->get_hashing_algorithm($hash); + if ($stored_hash_type == false) + { + return false; + } + + // Multiple hash passes needed + if (is_array($stored_hash_type)) + { + + return $this->helper->check_combined_hash($password, $stored_hash_type, $hash); + } + + return $stored_hash_type->check($password, $hash); + if ($stored_hash_type->get_type() !== $this->type) + { + // check with "old" hash and convert to new one + } + else + { + // check with default type + } + } +}