ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilBcryptPasswordEncoder.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2014 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once 'Services/Password/classes/encoders/class.ilBcryptPhpPasswordEncoder.php';
5
12{
16 const MIN_SALT_SIZE = 16;
17
21 const SALT_STORAGE_FILENAME = 'pwsalt.txt';
22
26 private $client_salt = null;
27
32
36 private $backward_compatibility = false;
37
41 private $data_directory = '';
42
47 public function __construct(array $config = array())
48 {
49 if (!empty($config)) {
50 foreach ($config as $key => $value) {
51 switch (strtolower($key)) {
52 case 'ignore_security_flaw':
53 $this->setIsSecurityFlawIgnored($value);
54 break;
55
56 case 'data_directory':
57 $this->setDataDirectory($value);
58 break;
59 }
60 }
61 }
62
63 parent::__construct($config);
64 }
65
69 protected function init()
70 {
71 $this->readClientSalt();
72 }
73
77 protected function isBcryptSupported()
78 {
79 return PHP_VERSION_ID >= 50307;
80 }
81
85 public function getDataDirectory()
86 {
88 }
89
94 {
95 $this->data_directory = $data_directory;
96 }
97
102 {
103 return (bool) $this->backward_compatibility;
104 }
105
111 {
112 $this->backward_compatibility = (bool) $backward_compatibility;
113 }
114
118 public function isSecurityFlawIgnored()
119 {
121 }
122
127 {
128 $this->is_security_flaw_ignored = (bool) $is_security_flaw_ignored;
129 }
130
134 public function getClientSalt()
135 {
136 return $this->client_salt;
137 }
138
143 {
144 $this->client_salt = $client_salt;
145 }
146
151 public function encodePassword($raw, $salt)
152 {
153 if (!$this->getClientSalt()) {
154 require_once 'Services/Password/exceptions/class.ilPasswordException.php';
155 throw new ilPasswordException('Missing client salt.');
156 }
157
158 if ($this->isPasswordTooLong($raw)) {
159 require_once 'Services/Password/exceptions/class.ilPasswordException.php';
160 throw new ilPasswordException('Invalid password.');
161 }
162
163 return $this->encode($raw, $salt);
164 }
165
169 public function isPasswordValid($encoded, $raw, $salt)
170 {
171 if (!$this->getClientSalt()) {
172 require_once 'Services/Password/exceptions/class.ilPasswordException.php';
173 throw new ilPasswordException('Missing client salt.');
174 }
175
176 return !$this->isPasswordTooLong($raw) && $this->check($encoded, $raw, $salt);
177 }
178
182 public function getName()
183 {
184 return 'bcrypt';
185 }
186
190 public function requiresSalt()
191 {
192 return true;
193 }
194
195
196
200 public function requiresReencoding($encoded)
201 {
202 return false;
203 }
204
212 protected function encode($raw, $user_secret)
213 {
214 $client_secret = $this->getClientSalt();
215 $hashed_password = hash_hmac('whirlpool', str_pad($raw, strlen($raw) * 4, sha1($user_secret), STR_PAD_BOTH), $client_secret, true);
216 $salt = substr(str_shuffle(str_repeat('./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 22)), 0, 22);
217
222 if ($this->isBcryptSupported() && !$this->isBackwardCompatibilityEnabled()) {
223 $prefix = '$2y$';
224 } else {
225 $prefix = '$2a$';
226 // check if the password contains 8-bit character
227 if (!$this->isSecurityFlawIgnored() && preg_match('/[\x80-\xFF]/', $raw)) {
228 require_once 'Services/Password/exceptions/class.ilPasswordException.php';
229 throw new ilPasswordException(
230 'The bcrypt implementation used by PHP can contain a security flaw ' .
231 'using passwords with 8-bit characters. ' .
232 'We suggest to upgrade to PHP 5.3.7+ or use passwords with only 7-bit characters.'
233 );
234 }
235 }
236
237 $salted_password = crypt($hashed_password, $prefix . $this->getCosts() . '$' . $salt);
238 if (strlen($salted_password) <= 13) {
239 require_once 'Services/Password/exceptions/class.ilPasswordException.php';
240 throw new ilPasswordException('Error during the bcrypt generation');
241 }
242
243 return $salted_password;
244 }
245
253 protected function check($encoded, $raw, $salt)
254 {
255 $hashed_password = hash_hmac('whirlpool', str_pad($raw, strlen($raw) * 4, sha1($salt), STR_PAD_BOTH), $this->getClientSalt(), true);
256 return crypt($hashed_password, substr($encoded, 0, 30)) == $encoded;
257 }
258
262 public function getClientSaltLocation()
263 {
264 return $this->getDataDirectory() . '/' . self::SALT_STORAGE_FILENAME;
265 }
266
270 private function readClientSalt()
271 {
272 if (is_file($this->getClientSaltLocation()) && is_readable($this->getClientSaltLocation())) {
273 $contents = file_get_contents($this->getClientSaltLocation());
274 if (strlen(trim($contents))) {
275 $this->setClientSalt($contents);
276 }
277 } else {
278 $this->generateClientSalt();
279 $this->storeClientSalt();
280 }
281 }
282
286 private function generateClientSalt()
287 {
288 require_once 'Services/Password/classes/class.ilPasswordUtils.php';
289 $this->setClientSalt(
290 substr(str_replace('+', '.', base64_encode(ilPasswordUtils::getBytes(self::MIN_SALT_SIZE))), 0, 22)
291 );
292 }
293
297 private function storeClientSalt()
298 {
299 $result = @file_put_contents($this->getClientSaltLocation(), $this->getClientSalt());
300 if (!$result) {
301 require_once 'Services/Password/exceptions/class.ilPasswordException.php';
302 throw new ilPasswordException(sprintf("Could not store the client salt in: %s. Please contact an administrator.", $this->getClientSaltLocation()));
303 }
304 }
305}
$result
An exception for terminatinating execution or to throw for unit testing.
isPasswordTooLong($password)
Checks if the password is too long.
setIsSecurityFlawIgnored($is_security_flaw_ignored)
setBackwardCompatibility($backward_compatibility)
Set the backward compatibility $2a$ instead of $2y$ for PHP 5.3.7+.
encodePassword($raw, $salt)
{{Encodes the raw password.string The encoded password}}
requiresReencoding($encoded)
{{{Returns whether or not the a encoded password needs to be re-encoded.boolean}}}
isPasswordValid($encoded, $raw, $salt)
{{Checks a raw password against an encoded password.The raw password has to be injected into the enco...
requiresSalt()
{{Returns whether or not the encoder requires a salt.boolean}}
check($encoded, $raw, $salt)
Verifies a bcrypt encoded string.
encode($raw, $user_secret)
Generates a bcrypt encoded string.
Class for user password exception handling in ILIAS.
static getBytes($length)
Generate random bytes using OpenSSL or Mcrypt and mt_rand() as fallback.
$key
Definition: croninfo.php:18
$config
Definition: bootstrap.php:15