1: <?php
2: /**
3: * PHP Password Library
4: *
5: * @package PHPassLib\Hashes
6: * @author Ryan Chouinard <rchouinard@gmail.com>
7: * @copyright Copyright (c) 2012, Ryan Chouinard
8: * @license MIT License - http://www.opensource.org/licenses/mit-license.php
9: * @version 3.0.0-dev
10: */
11:
12: namespace PHPassLib\Hash;
13:
14: use PHPassLib\Hash;
15: use PHPassLib\Utilities;
16: use PHPassLib\Exception\InvalidArgumentException;
17:
18: /**
19: * MD5 Crypt Module
20: *
21: * MD5 Crypt was designed to replace DES Crypt for FreeBSD. The algorithm is
22: * now considered weak by it's author, Poul-Henning Kamp. It should not be used
23: * in new applications. More information about the algorithm's deprecation can
24: * be found at http://phk.freebsd.dk/sagas/md5crypt_eol.html.
25: *
26: * Supported parameters:
27: *
28: * <ul>
29: * <li><b>salt:</b> Optional salt string. If provided, it must be an 8
30: * character string containing only characters in the regex range
31: * [./0-9A-Za-z]. It is highly recommended that this parameter be left blank,
32: * in which case the library will generate a suitable salt for you.</li>
33: * </ul>
34: *
35: * This module uses PHP's native crypt() function, which has had native support
36: * for MD5 Crypt since 5.3.0.
37: *
38: * @package PHPassLib\Hashes
39: * @author Ryan Chouinard <rchouinard@gmail.com>
40: * @copyright Copyright (c) 2012, Ryan Chouinard
41: * @license MIT License - http://www.opensource.org/licenses/mit-license.php
42: */
43: class MD5Crypt implements Hash
44: {
45:
46: /**
47: * Generate a config string from an array.
48: *
49: * @param array $config Array of configuration options.
50: * @return string Configuration string.
51: * @throws InvalidArgumentException Throws an InvalidArgumentException if
52: * any passed-in configuration options are invalid.
53: */
54: public static function genConfig(array $config = array ())
55: {
56: $defaults = array (
57: 'salt' => Utilities::encode64(Utilities::genRandomBytes(6)),
58: );
59: $config = array_merge($defaults, array_change_key_case($config, CASE_LOWER));
60:
61: $string = '*1';
62: if (self::validateOptions($config)) {
63: $string = sprintf('$1$%s', $config['salt']);
64: }
65:
66: return $string;
67: }
68:
69: /**
70: * Parse a config string into an array.
71: *
72: * @param string $config Configuration string.
73: * @return array Array of configuration options or false on failure.
74: */
75: public static function parseConfig($config)
76: {
77: $options = false;
78: $matches = array ();
79: if (preg_match('/^\$1\$([\.\/0-9A-Za-z]{0,8})\$?/', $config, $matches)) {
80: $options = array (
81: 'salt' => $matches[1],
82: );
83:
84: try {
85: self::validateOptions($options);
86: } catch (InvalidArgumentException $e) {
87: $options = false;
88: }
89: }
90:
91: return $options;
92: }
93:
94: /**
95: * Generate a password hash using a config string.
96: *
97: * @param string $password Password string.
98: * @param string $config Configuration string.
99: * @return string Returns the hash string on success. On failure, one of
100: * *0 or *1 is returned.
101: */
102: public static function genHash($password, $config)
103: {
104: $hash = crypt($password, $config);
105: if (!preg_match('/^\$1\$[\.\/0-9A-Za-z]{0,8}\$[\.\/0-9A-Za-z]{22}$/', $hash)) {
106: $hash = ($config == '*0') ? '*1' : '*0';
107: }
108:
109: return $hash;
110: }
111:
112: /**
113: * Generate a password hash using a config string or array.
114: *
115: * @param string $password Password string.
116: * @param string|array $config Optional config string or array of options.
117: * @return string Returns the hash string on success. On failure, one of
118: * *0 or *1 is returned.
119: * @throws InvalidArgumentException Throws an InvalidArgumentException if
120: * any passed-in configuration options are invalid.
121: */
122: public static function hash($password, $config = array ())
123: {
124: if (is_array($config)) {
125: $config = self::genConfig($config);
126: }
127:
128: return self::genHash($password, $config);
129: }
130:
131: /**
132: * Verify a password against a hash string.
133: *
134: * @param string $password Password string.
135: * @param string $hash Hash string.
136: * @return boolean Returns true if the password matches, false otherwise.
137: */
138: public static function verify($password, $hash)
139: {
140: return ($hash === self::hash($password, $hash));
141: }
142:
143: /**
144: * @param array $options
145: * @return boolean
146: * @throws InvalidArgumentException
147: */
148: protected static function validateOptions(array $options)
149: {
150: $options = array_change_key_case($options, CASE_LOWER);
151: foreach ($options as $option => $value) switch ($option) {
152:
153: case 'salt':
154: if (!preg_match('/^[\.\/0-9A-Za-z]{0,8}$/', $value)) {
155: throw new InvalidArgumentException('Invalid salt parameter');
156: }
157: break;
158:
159: default:
160: break;
161:
162: }
163:
164: return true;
165: }
166:
167: }
168: