Skip to content
This repository was archived by the owner on Nov 24, 2023. It is now read-only.

Commit 05aa79f

Browse files
author
maxence d'Espeuilles
committed
first commit
0 parents  commit 05aa79f

9 files changed

Lines changed: 771 additions & 0 deletions
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Mdespeuilles\DrupalPasswordEncoderBundle\DependencyInjection;
4+
5+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6+
use Symfony\Component\Config\Definition\ConfigurationInterface;
7+
8+
/**
9+
* This is the class that validates and merges configuration from your app/config files.
10+
*
11+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html}
12+
*/
13+
class Configuration implements ConfigurationInterface
14+
{
15+
/**
16+
* {@inheritdoc}
17+
*/
18+
public function getConfigTreeBuilder()
19+
{
20+
$treeBuilder = new TreeBuilder();
21+
$rootNode = $treeBuilder->root('mdespeuilles_drupal_password_encoder');
22+
23+
// Here you should define the parameters that are allowed to
24+
// configure your bundle. See the documentation linked above for
25+
// more information on that topic.
26+
27+
return $treeBuilder;
28+
}
29+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Mdespeuilles\DrupalPasswordEncoderBundle\DependencyInjection;
4+
5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
6+
use Symfony\Component\Config\FileLocator;
7+
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
8+
use Symfony\Component\DependencyInjection\Loader;
9+
10+
/**
11+
* This is the class that loads and manages your bundle configuration.
12+
*
13+
* @link http://symfony.com/doc/current/cookbook/bundles/extension.html
14+
*/
15+
class MdespeuillesDrupalPasswordEncoderExtension extends Extension
16+
{
17+
/**
18+
* {@inheritdoc}
19+
*/
20+
public function load(array $configs, ContainerBuilder $container)
21+
{
22+
$configuration = new Configuration();
23+
$config = $this->processConfiguration($configuration, $configs);
24+
25+
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
26+
$loader->load('services.yml');
27+
}
28+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Mdespeuilles\DrupalPasswordEncoderBundle;
4+
5+
use Symfony\Component\HttpKernel\Bundle\Bundle;
6+
7+
class MdespeuillesDrupalPasswordEncoderBundle extends Bundle
8+
{
9+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# DrupalPasswordEncoder

Resources/config/services.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
services:
2+
mdespeuilles_drupal_password_encoder:
3+
class: Mdespeuilles\DrupalPasswordEncoderBundle\Services\DrupalPasswordEncoder

Services/DrupalPasswordEncoder.php

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
<?php
2+
namespace Mdespeuilles\DrupalPasswordEncoderBundle\Services;
3+
4+
use Mdespeuilles\DrupalPasswordEncoderBundle\Services\Password\PhpassHashedPassword;
5+
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
6+
7+
class DrupalPasswordEncoder implements PasswordEncoderInterface
8+
{
9+
const DRUPAL_HASH_COUNT = 15;
10+
11+
protected $drupalPasswordService;
12+
13+
public function __construct()
14+
{
15+
$this->drupalPasswordService = new PhpassHashedPassword(self::DRUPAL_HASH_COUNT);
16+
}
17+
18+
public function encodePassword($password, $salt)
19+
{
20+
return $this->drupalPasswordService->hash($password);
21+
//return $this->_password_crypt('sha512', $password, $this->_password_generate_salt(self::DRUPAL_HASH_COUNT));
22+
}
23+
24+
public function isPasswordValid($encoded, $raw, $salt)
25+
{
26+
return $this->drupalPasswordService->check($raw, $encoded);
27+
//return $this->user_check_password($raw, $encoded);
28+
}
29+
30+
private function _password_generate_salt($count_log2) {
31+
$output = '$S$';
32+
// We encode the final log2 iteration count in base 64.
33+
$itoa64 = $this->_password_itoa64();
34+
$output .= $itoa64[$count_log2];
35+
// 6 bytes is the standard salt for a portable phpass hash.
36+
$output .= $this->_password_base64_encode($this->drupal_random_bytes(6), 6);
37+
return $output;
38+
}
39+
40+
private function _password_itoa64() {
41+
return './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
42+
}
43+
44+
private function _password_base64_encode($input, $count) {
45+
$output = '';
46+
$i = 0;
47+
$itoa64 = $this->_password_itoa64();
48+
do {
49+
$value = ord($input[$i++]);
50+
$output .= $itoa64[$value & 0x3f];
51+
if ($i < $count) {
52+
$value |= ord($input[$i]) << 8;
53+
}
54+
$output .= $itoa64[($value >> 6) & 0x3f];
55+
if ($i++ >= $count) {
56+
break;
57+
}
58+
if ($i < $count) {
59+
$value |= ord($input[$i]) << 16;
60+
}
61+
$output .= $itoa64[($value >> 12) & 0x3f];
62+
if ($i++ >= $count) {
63+
break;
64+
}
65+
$output .= $itoa64[($value >> 18) & 0x3f];
66+
} while ($i < $count);
67+
68+
return $output;
69+
}
70+
71+
private function drupal_random_bytes($count) {
72+
// $random_state does not use drupal_static as it stores random bytes.
73+
static $random_state, $bytes, $has_openssl;
74+
75+
$missing_bytes = $count - strlen($bytes);
76+
77+
if ($missing_bytes > 0) {
78+
// PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
79+
// locking on Windows and rendered it unusable.
80+
if (!isset($has_openssl)) {
81+
$has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=') && function_exists('openssl_random_pseudo_bytes');
82+
}
83+
84+
// openssl_random_pseudo_bytes() will find entropy in a system-dependent
85+
// way.
86+
if ($has_openssl) {
87+
$bytes .= openssl_random_pseudo_bytes($missing_bytes);
88+
}
89+
90+
// Else, read directly from /dev/urandom, which is available on many *nix
91+
// systems and is considered cryptographically secure.
92+
elseif ($fh = @fopen('/dev/urandom', 'rb')) {
93+
// PHP only performs buffered reads, so in reality it will always read
94+
// at least 4096 bytes. Thus, it costs nothing extra to read and store
95+
// that much so as to speed any additional invocations.
96+
$bytes .= fread($fh, max(4096, $missing_bytes));
97+
fclose($fh);
98+
}
99+
100+
// If we couldn't get enough entropy, this simple hash-based PRNG will
101+
// generate a good set of pseudo-random bytes on any system.
102+
// Note that it may be important that our $random_state is passed
103+
// through hash() prior to being rolled into $output, that the two hash()
104+
// invocations are different, and that the extra input into the first one -
105+
// the microtime() - is prepended rather than appended. This is to avoid
106+
// directly leaking $random_state via the $output stream, which could
107+
// allow for trivial prediction of further "random" numbers.
108+
if (strlen($bytes) < $count) {
109+
// Initialize on the first call. The contents of $_SERVER includes a mix of
110+
// user-specific and system information that varies a little with each page.
111+
if (!isset($random_state)) {
112+
$random_state = print_r($_SERVER, TRUE);
113+
if (function_exists('getmypid')) {
114+
// Further initialize with the somewhat random PHP process ID.
115+
$random_state .= getmypid();
116+
}
117+
$bytes = '';
118+
}
119+
120+
do {
121+
$random_state = hash('sha256', microtime() . mt_rand() . $random_state);
122+
$bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
123+
}
124+
while (strlen($bytes) < $count);
125+
}
126+
}
127+
$output = substr($bytes, 0, $count);
128+
$bytes = substr($bytes, $count);
129+
return $output;
130+
}
131+
132+
private function _password_get_count_log2($setting) {
133+
$itoa64 = $this->_password_itoa64();
134+
return strpos($itoa64, $setting[3]);
135+
}
136+
137+
private function user_check_password($password, $hash) {
138+
if (substr($hash, 0, 2) == 'U$') {
139+
// This may be an updated password from user_update_7000(). Such hashes
140+
// have 'U' added as the first character and need an extra md5().
141+
$stored_hash = substr($hash, 1);
142+
$password = md5($password);
143+
}
144+
else {
145+
$stored_hash = $hash;
146+
}
147+
148+
$type = substr($stored_hash, 0, 3);
149+
switch ($type) {
150+
case '$S$':
151+
// A normal Drupal 7 password using sha512.
152+
$hash = $this->_password_crypt('sha512', $password, $stored_hash);
153+
break;
154+
case '$H$':
155+
// phpBB3 uses "$H$" for the same thing as "$P$".
156+
case '$P$':
157+
// A phpass password generated using md5. This is an
158+
// imported password or from an earlier Drupal version.
159+
$hash = $this->_password_crypt('md5', $password, $stored_hash);
160+
break;
161+
default:
162+
return FALSE;
163+
}
164+
return ($hash && $stored_hash == $hash);
165+
}
166+
167+
private function _password_crypt($algo, $password, $setting) {
168+
// Prevent DoS attacks by refusing to hash large passwords.
169+
if (strlen($password) > 512) {
170+
return FALSE;
171+
}
172+
// The first 12 characters of an existing hash are its setting string.
173+
$setting = substr($setting, 0, 12);
174+
175+
if ($setting[0] != '$' || $setting[2] != '$') {
176+
return FALSE;
177+
}
178+
$count_log2 = $this->_password_get_count_log2($setting);
179+
// Hashes may be imported from elsewhere, so we allow != DRUPAL_HASH_COUNT
180+
if ($count_log2 < 7 || $count_log2 > 30) {
181+
return FALSE;
182+
}
183+
$salt = substr($setting, 4, 8);
184+
// Hashes must have an 8 character salt.
185+
if (strlen($salt) != 8) {
186+
return FALSE;
187+
}
188+
189+
// Convert the base 2 logarithm into an integer.
190+
$count = 1 << $count_log2;
191+
192+
// We rely on the hash() function being available in PHP 5.2+.
193+
$hash = hash($algo, $salt . $password, TRUE);
194+
do {
195+
$hash = hash($algo, $hash . $password, TRUE);
196+
} while (--$count);
197+
198+
$len = strlen($hash);
199+
$output = $setting . $this->_password_base64_encode($hash, $len);
200+
// _password_base64_encode() of a 16 byte MD5 will always be 22 characters.
201+
// _password_base64_encode() of a 64 byte sha512 will always be 86 characters.
202+
$expected = 12 + ceil((8 * $len) / 6);
203+
return (strlen($output) == $expected) ? substr($output, 0, 55) : FALSE;
204+
}
205+
}

0 commit comments

Comments
 (0)