<?php /** * * @package phpBB3 * @copyright (c) 2013 phpBB Group * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 * */ /** * @ignore */ if (!defined('IN_PHPBB')) { exit; } /** * @package crypto */ class phpbb_crypto_manager { /** * Default hashing method */ protected $type = false; /** * Hashing algorithm types */ protected $type_map = false; /** * Crypto helper * @var phpbb_crypto_helper */ protected $helper; /** * phpBB configuration * @var phpbb_config */ protected $config; /** * phpBB compiled container * @var service_container */ protected $container; /** * Construct a crypto object * * @param phpbb_config $config phpBB configuration */ public function __construct($config, $container, $hashing_algorithms) { $this->config = $config; $this->container = $container; $this->type = 'crypto.driver.bcrypt_2y'; // 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 } } }