Subversion-Projekte lars-tiefland.ci

Revision

Revision 2254 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
2257 lars 9
 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology
68 lars 10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
2257 lars 32
 * @copyright	Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
68 lars 33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 3.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * PHP ext/standard/password compatibility package
42
 *
43
 * @package		CodeIgniter
44
 * @subpackage	CodeIgniter
45
 * @category	Compatibility
46
 * @author		Andrey Andreev
47
 * @link		https://codeigniter.com/user_guide/
48
 * @link		http://php.net/password
49
 */
50
 
51
// ------------------------------------------------------------------------
52
 
1257 lars 53
if (is_php('5.5') OR ! defined('CRYPT_BLOWFISH') OR CRYPT_BLOWFISH !== 1 OR defined('HHVM_VERSION'))
68 lars 54
{
55
	return;
56
}
57
 
58
// ------------------------------------------------------------------------
59
 
60
defined('PASSWORD_BCRYPT') OR define('PASSWORD_BCRYPT', 1);
61
defined('PASSWORD_DEFAULT') OR define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
62
 
63
// ------------------------------------------------------------------------
64
 
65
if ( ! function_exists('password_get_info'))
66
{
67
	/**
68
	 * password_get_info()
69
	 *
70
	 * @link	http://php.net/password_get_info
71
	 * @param	string	$hash
72
	 * @return	array
73
	 */
74
	function password_get_info($hash)
75
	{
76
		return (strlen($hash) < 60 OR sscanf($hash, '$2y$%d', $hash) !== 1)
77
			? array('algo' => 0, 'algoName' => 'unknown', 'options' => array())
78
			: array('algo' => 1, 'algoName' => 'bcrypt', 'options' => array('cost' => $hash));
79
	}
80
}
81
 
82
// ------------------------------------------------------------------------
83
 
84
if ( ! function_exists('password_hash'))
85
{
86
	/**
87
	 * password_hash()
88
	 *
89
	 * @link	http://php.net/password_hash
90
	 * @param	string	$password
91
	 * @param	int	$algo
92
	 * @param	array	$options
93
	 * @return	mixed
94
	 */
95
	function password_hash($password, $algo, array $options = array())
96
	{
2107 lars 97
		static $func_overload;
98
		isset($func_overload) OR $func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));
68 lars 99
 
100
		if ($algo !== 1)
101
		{
102
			trigger_error('password_hash(): Unknown hashing algorithm: '.(int) $algo, E_USER_WARNING);
103
			return NULL;
104
		}
105
 
106
		if (isset($options['cost']) && ($options['cost'] < 4 OR $options['cost'] > 31))
107
		{
108
			trigger_error('password_hash(): Invalid bcrypt cost parameter specified: '.(int) $options['cost'], E_USER_WARNING);
109
			return NULL;
110
		}
111
 
2107 lars 112
		if (isset($options['salt']) && ($saltlen = ($func_overload ? mb_strlen($options['salt'], '8bit') : strlen($options['salt']))) < 22)
68 lars 113
		{
114
			trigger_error('password_hash(): Provided salt is too short: '.$saltlen.' expecting 22', E_USER_WARNING);
115
			return NULL;
116
		}
117
		elseif ( ! isset($options['salt']))
118
		{
119
			if (function_exists('random_bytes'))
120
			{
121
				try
122
				{
123
					$options['salt'] = random_bytes(16);
124
				}
125
				catch (Exception $e)
126
				{
127
					log_message('error', 'compat/password: Error while trying to use random_bytes(): '.$e->getMessage());
128
					return FALSE;
129
				}
130
			}
131
			elseif (defined('MCRYPT_DEV_URANDOM'))
132
			{
133
				$options['salt'] = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
134
			}
135
			elseif (DIRECTORY_SEPARATOR === '/' && (is_readable($dev = '/dev/arandom') OR is_readable($dev = '/dev/urandom')))
136
			{
137
				if (($fp = fopen($dev, 'rb')) === FALSE)
138
				{
139
					log_message('error', 'compat/password: Unable to open '.$dev.' for reading.');
140
					return FALSE;
141
				}
142
 
143
				// Try not to waste entropy ...
144
				is_php('5.4') && stream_set_chunk_size($fp, 16);
145
 
146
				$options['salt'] = '';
2107 lars 147
				for ($read = 0; $read < 16; $read = ($func_overload) ? mb_strlen($options['salt'], '8bit') : strlen($options['salt']))
68 lars 148
				{
149
					if (($read = fread($fp, 16 - $read)) === FALSE)
150
					{
151
						log_message('error', 'compat/password: Error while reading from '.$dev.'.');
152
						return FALSE;
153
					}
154
					$options['salt'] .= $read;
155
				}
156
 
157
				fclose($fp);
158
			}
159
			elseif (function_exists('openssl_random_pseudo_bytes'))
160
			{
161
				$is_secure = NULL;
162
				$options['salt'] = openssl_random_pseudo_bytes(16, $is_secure);
163
				if ($is_secure !== TRUE)
164
				{
165
					log_message('error', 'compat/password: openssl_random_pseudo_bytes() set the $cryto_strong flag to FALSE');
166
					return FALSE;
167
				}
168
			}
169
			else
170
			{
171
				log_message('error', 'compat/password: No CSPRNG available.');
172
				return FALSE;
173
			}
174
 
175
			$options['salt'] = str_replace('+', '.', rtrim(base64_encode($options['salt']), '='));
176
		}
177
		elseif ( ! preg_match('#^[a-zA-Z0-9./]+$#D', $options['salt']))
178
		{
179
			$options['salt'] = str_replace('+', '.', rtrim(base64_encode($options['salt']), '='));
180
		}
181
 
182
		isset($options['cost']) OR $options['cost'] = 10;
183
 
184
		return (strlen($password = crypt($password, sprintf('$2y$%02d$%s', $options['cost'], $options['salt']))) === 60)
185
			? $password
186
			: FALSE;
187
	}
188
}
189
 
190
// ------------------------------------------------------------------------
191
 
192
if ( ! function_exists('password_needs_rehash'))
193
{
194
	/**
195
	 * password_needs_rehash()
196
	 *
197
	 * @link	http://php.net/password_needs_rehash
198
	 * @param	string	$hash
199
	 * @param	int	$algo
200
	 * @param	array	$options
201
	 * @return	bool
202
	 */
203
	function password_needs_rehash($hash, $algo, array $options = array())
204
	{
205
		$info = password_get_info($hash);
206
 
207
		if ($algo !== $info['algo'])
208
		{
209
			return TRUE;
210
		}
211
		elseif ($algo === 1)
212
		{
213
			$options['cost'] = isset($options['cost']) ? (int) $options['cost'] : 10;
214
			return ($info['options']['cost'] !== $options['cost']);
215
		}
216
 
217
		// Odd at first glance, but according to a comment in PHP's own unit tests,
218
		// because it is an unknown algorithm - it's valid and therefore doesn't
219
		// need rehashing.
220
		return FALSE;
221
	}
222
}
223
 
224
// ------------------------------------------------------------------------
225
 
226
if ( ! function_exists('password_verify'))
227
{
228
	/**
229
	 * password_verify()
230
	 *
231
	 * @link	http://php.net/password_verify
232
	 * @param	string	$password
233
	 * @param	string	$hash
234
	 * @return	bool
235
	 */
236
	function password_verify($password, $hash)
237
	{
238
		if (strlen($hash) !== 60 OR strlen($password = crypt($password, $hash)) !== 60)
239
		{
240
			return FALSE;
241
		}
242
 
243
		$compare = 0;
244
		for ($i = 0; $i < 60; $i++)
245
		{
246
			$compare |= (ord($password[$i]) ^ ord($hash[$i]));
247
		}
248
 
249
		return ($compare === 0);
250
	}
251
}